使用交换机做DHCP服务器

什么是DHCP服务器呢?为什么我们要配置DHCP服务器呢?

  所谓的DHCP就是Dynamic Host Configuration Protocol的缩写,即动态主机配置协议,它是TCP/IP协议簇中的一种,主要作用给网络中其他电脑动态分配IP地址之用。


  纯粹地谈DHCP协议也许大家会觉得很陌生,但是说到“自动获取IP地址”,各位有装机经验的朋友可能就比较熟悉了。在windows 2000下,我们打开“本地连接 属性”对话框之后,双击“internet 协议 (TCP/IP)”,打开“internet 协议(TCP/IP) 属性”对话框中,就有一项“自动获得 IP 地址”(当然我们也可以直接指定固定IP给电脑)。然而为什么我们可以使电脑来自动获取IP呢?试想一下,如果每台电脑可以自动获取任意IP的话,那岂不是IP地址严重冲突,网络世界大乱。然而现实生活中使用自动获取IP的网络并没有象我们想象中的一片混乱,为什么呢?原因就是在我们的网络中存在这一个DHCP服务器,这个DHCP服务器管理着网络中的IP地址资源,使得各IP地址合理地分配给网络中的每一台有需要的主机。


  为什么我们有时候需要DHCP服务器来使IP自动分配呢?举个例子来说明一下:假如在某单位中有400台电脑,但是在任何一个时刻,要上网的电脑不会超过255台。如果使用固定IP的话,那么我们就需要为这个单位申请400IP才够用;但是如果我们使用动态IP分配的话,我们只要申请255IP就已经足够了,节省了145IP资源的费用。DHCP服务就是特别试用于这种IP资源使用密度不高的情况。


  然而,常规的方法来讲,需要专门配置一台服务器来做DHCP服务器,这样无疑又增加了网络耗费。据笔者的经验,有时在一些网络低层设备中(如路由器、交换机等)里面整合了DHCP服务,一般的小型网络里,我们完全可以利用网络中的这些网络设备是上的DHCP服务来配置我们自己的DHCP服务器,而不需要另外专门配置一台服务器来做DHCP服务。


  下面我就以cisco交换机(其他款交换机没试过)为例子,说说具体做法:


  假设在某网络中有400台主机,其中申请了255个以供分配的IP,其范围从202.38.X.0~202.38.X.254,其中202.38.X.1是用来分配给网关的,该网络里有三台服务器ABC,其需要固定的IP,分别为202.38.X.2202.38.X.3202.38.X.4,其他的所有IP均供动态分配之用。网络拓扑结构如下
:

  



  telnet登陆上交换机,进入交换机命令行;

  启动DHCP服务:

  service DHCP

  对于要固定分配的IP,我们需要先将其约定出来:

  ip dhcp exclude-address 202.38.X.2 202.38.X.3 202.38.X.4

  在分配地址前,先将测试一下该地址,如果该地址有人在用,则分配其他地址,避免IP地址的冲突:

  ip dhcp ping packets 3

  设置DHCP地址池全局变量,

  ip dhcp pool 1

  设置动态分配的IP地址范围、子网掩码:

  network 202.38.X.0 255.255.255.0 //动态分配IP范围,这里给定的是202.38.X.0255.255.255.0,也就是该段所有IP

  设置网关:

  default-router 202.38.X.1

  设置DNS服务器地址

  dns-server 202.38.193.33 202.112.17.33

  就这么简单,一个cisco交换机上的DHCP服务器配置完成了。请注意,并不是所有的交换机上都带有了DHCP服务,笔者使用的是cisco交换机,所以请大家购买的时候根据用途多加注意了。

posted @ 2006-02-11 10:31 青蛙學堂 阅读(288) | 评论 (0)编辑 收藏

CREATE INDEX

     摘要: 为给定表或视图创建索引。   只有表或视图的所有者才能为表创建索引。表或视图的所有者可以随时创建索引,无论表中是否有数据。可以通过指定限定的数据库名称,为另一个数据库中的表或视图创建索引。  语法   CREATE [ UNIQUE ] [ CLUSTERED | NONCLUSTERED ] INDEX index_name   ON { table | view } ( column [ AS...  阅读全文

posted @ 2006-02-10 08:37 青蛙學堂 阅读(3671) | 评论 (2)编辑 收藏

WINS

Windows Internet Name Service
微软开发的域名服务系统。

WINSWindows Internet Name ServerWindows网际名字服务)的简称。WINSNetBIOS名字提供名字注册、更新、释放和转换服务,这些服务允许WINS服务器维护一个将NetBIOS名链接到IP地址的动态数据库,大大减轻了对网络交通的负担。

.我们为什么需要WINS服务

在默认状态中,网络上的每一台计算机的NetBIOS名字是通过广播的方式来提供更新的,也就是说,假如网络上有n台计算机,那么每一台计算机就要广播n-1次,对于小型网络来说,这似乎并不影响网络交通,但是当大型网络来说,加重了网络的负担。因此WINS对大中型企业来说尤其重要。

.WINS工作原理

上面说过,WINS服务器为客户端提供名字注册了、更新、释放和转换服务,下面就详细介绍这四个基本服务的工作原理:

1.名字注册

名字注册就是客户端从WINS服务器获得信息的过程,在WINS服务中,名字注册是动态的。

当一个客户端启动时,它向所配置的WINS服务器发送一个名字注册信息(包括了客户机的IP地址和计算机名),如果WINS服务器正在运行,并且没有没有其它客户计算机注册了相同的名字,服务器就向客户端计算机返还一个成功注册的消息(包括了名字注册的存活期----TTL)。

IP地址一样,每个计算机都要求有唯一的计算机名,否则就无法通信。如果名字已经被其它计算机注册了,WINS服务将会验证该名字是否正在使用。如果该名字正在使用则注册失败(发回一个负确认的信息),否则就可以继续注册。

2.名字更新

因为客户端被分配了一个TTL(存活期),所有它的注册也有一定的期限,过了这个期限,WINS服务器将从数据库中删除这个名字的注册信息。它的过程是这样的:

1.在过了存活期的1/8后,客户端开始不断试图更新它的名字注册,如果收到不到任何响应,WINS客户端每过2分钟重复更新浓度,直到存活期过了一半。

2.当存活期过了一半时,WINS客户端将尝试与次选WINS服务器更新它的租约,它的过程与首选WINS服务器一样。

3.如果时间过了一半后仍然没有成功的话,该客户端又回到它的首选WINS服务器了。

在该过程中,不管是与首选还是次选WINS服务器,一旦名字注册成功之后,该WINS客户端的名字注册将被提供一个新的TTL值。


3.
名字释放

在客户端的正常关机过程中,WINS客户端向WINS服务器发送一个名字释放的请求,以请求释放其映射在WINS服务器数据库中的IP地址和NetBIOS名字。收到释放请求后,WINS服务器验证一下在它的数据库中是否有该IP地址和NetBIOS名,如果有就可以正常释放了,否则就会出现错误(WINS服务器向WINS客户端发送一个负响应)。

如果计算机没有正常关闭,WINS服务器将不知道其名字已经释放了,则该名字将不会失效,直到WINS名字注册记录过期。

4.名字解析

当客户端在许多网络操作中需要WINS服务器解析名字,例如当使用网络上其它计算机的共享文件时,为了得到共享文件,用户需要指定两件事:系统名和共享名,而系统名就需要转换成IP地址。

名字解析过程是这样的:

1.当客户端计算机想要转换一个名字时,它首先检查本地NetBIOS名字缓存器。

2.如果名字不在本地NetBIOS名字缓存器中,便发送一个名字查询到首选WINS服务器(每隔15秒发送一次,共发三次),如果请求失败,则向次选WINS发送同样的请求。

3.如果都失败了,那么名字解析可以通过其它途径来转换(例如本地广播、lmhosts文件和hosts文件、或者DNS来进行名字解析。

.WINS服务器和客户端的需求

1.Microsoft对服务器的需求

1.至少提供一个首选WINS服务器和一个次选WINS服务器来提供容错功能。

2.一个WINS服务器每分钟可以处理近1500个名字注册和约4500个名字查询。因此我们强烈建议你一个首选和一个次选WINS服务器可以带动10000个客户端。

3.如果WINS服务器与客户机不在同一个子网上,就要考虑到路由器的性能了。

2.客户端的需求

几乎所有的支持网络互联的Microsoft客户端都可以是WINS客户端,下面列出了可以与WINS一起工作的客户端:

*Windows NT Server 3.5x,4.0

*Windows NT Workstation 3.5x,4.0

*Windows 9x/me/2000

*Windows for Workgroups with TCP/IP-32

*Microsoft Network Client 3.0 for MS-DOS

*LAN Manager 2.2c for MS-DOS

基于DOS的客户端也可以用WINS服务器进行名字解析,但你必须为它们在WINS服务器中添加静态词条。

2000以后wins几乎被抛弃

posted @ 2006-02-09 19:56 青蛙學堂 阅读(248) | 评论 (0)编辑 收藏

Trigger iud

满足更新条件的行都触发......


可以在trigger中使用它的两个逻辑表 inserted和deleted
在update时,表中的老数所会保存在deleted中
>>>>>>>>>>>>>>要新增加的数据会保存在inserted中
所以通过这两个表可以确定,你要update的是那一行


樓上說的是sqlserver中的吧
oracle中如果想知道更新行欄位的新舊值,有二個關鍵詞 (:old_col,:new_col)


小弟知道,
:old.fieldname  
:new.fieldname
就可以访问被更新的,和更新的记录了。
那只是针对一条,如果我更新多行呢?


那就把:old.fieldname  /:new.fieldname 作为条件,再写一个update语句,如果在一张表操作,可能会遇到变异表的问题,建议还是把触发器建到一个临时表中去。


行级触发器

posted @ 2006-02-08 19:16 青蛙學堂 阅读(547) | 评论 (2)编辑 收藏

trigger问题收藏

下面是一个oracle的trigger,创建的时候没有问题,但是update操作的时候,
出现ORA-04091错误,也就是对行触发器来讲,对当前正在改变的表不允许做select操作,但是如果不用行触发器的话,无法知道当前改变的列的值,哪位高人能指点一二,万分感谢。
CREATE OR REPLACE TRIGGER TRIGGER_PING_MONITOR
AFTER DELETE OR INSERT OR UPDATE OF RecordStatus ON PINGMONITOR_ADMIN
FOR EACH ROW
DECLARE
v_num  NUMBER;
BEGIN

SELECT COUNT(*) INTO v_num FROM PINGMONITOR_ADMIN WHERE PINGMONITOR_ADMIN.PingMonitorID=:NEW.PingMonitorID AND PINGMONITOR_ADMIN.RecordStatus=0;
IF v_num>;0 THEN
UPDATE NODE_ADMIN SET PingMonitor = 1   
WHERE NODE_ADMIN.NodeID=:NEW.PingMonitorID;
ELSE
UPDATE NODE_ADMIN SET PingMonitor = 0   
WHERE NODE_ADMIN.NodeID=:NEW.PingMonitorID;
END IF;

END TRIGGER_PING_MONITOR;
/


SQL>; desc PINGMONITOR_ADMIN ;
名前                                      NULL?    型
----------------------------------------- -------- -------------
PINGMONITORID                             NOT NULL VARCHAR2(15)
PINGMONITORCNT                            NOT NULL NUMBER(2)
RECORDSTATUS                                       NUMBER(1)
RECORDUPDATETIME                                   DATE
RECORDCREATETIME                                   DATE

SQL>; desc NODE_ADMIN
名前                                      NULL?    型
----------------------------------------- -------- ------------------
NODEID                                    NOT NULL VARCHAR2(15)
IP                                                 VARCHAR2(40)
GLOBALIP                                           VARCHAR2(40)
HOSTNAME                                           VARCHAR2(64)
PINGMONITOR                                        NUMBER(1)
SERVICEPORTMONITOR                                 NUMBER(1)
PROCESSMONITOR                                     NUMBER(1)
RESOURCEMONITOR                                    NUMBER(1)
LOGMONITOR                                         NUMBER(1)
SNMP_TRAPMONITOR                                   NUMBER(1)
RECORDSTATUS                                       NUMBER(1)
RECORDUPDATETIME                                   DATE
RECORDCREATETIME                                   DATE
OL.O 2003-4-3 03:56
trigger问题求助 

你的PINGMONITOR_ADMIN.PingMonitorID是primary key吗?如是
把IF v_num>;0 THEN 改为
if :NEW.PINGMONITOR_ADMIN.RecordStatus=0 then
如不是primary key 那是哪一个
能否把你的意图写一下,表中是否有重复行
一般来说insert or update tigger写在一起的话,可用
if inserting then
--如果是插入
--这时候可以引用:NEW.PINGMONITOR_ADMIN.RecordStatus,但:OLD.PINGMONITOR_ADMIN.RecordStatus是空的
else
--如果是update
--这时:OLD,:NEW都可以引用
end if
我昨天也遇上这个问题,确实不能触发器上对触发表做SELECT

kasyunn 2003-4-3 05:05
trigger问题求助 

谢谢你的回答.

PINGMONITOR_ADMIN.PingMonitorID和PINGMONITOR_ADMIN.PINGMONITORCNT是primary key
可能是我没说清楚.

是UPDATE或者DELETE的时候,我需要知道PINGMONITOR_ADMIN里符合条件的
记录有没有再去更新NODE_ADMIN.

你的解决方法只能对应UPDATE一条记录的情况.这时候我并不能知道PINGMONITOR_ADMIN里符合条件的记录有没有.

OL.O 2003-4-3 05:44
trigger问题求助 

你根本不用去判断
假设你的update或delete是由
update(or delete)  PINGMONITOR_ADMIN set  PINGMONITOR_ADMIN.RecordStatus=0 where PINGMONITOR_ADMIN.PingMonitorID=条件 and  PINGMONITOR_ADMIN.RecordStatus=0;来触发的
那么你直接引用:NEW和:OLD就行了
注意:delete 是没有 :NEW的
如不是上面的假设触发条件,那你把可能的引起触发的语句都写一下行吗?
包括insert,update,delete

kasyunn 2003-4-3 06:03
trigger问题求助 

问题是我不知道update,delete是的where条件.

就个例子:
PINGMONITOR_ADMIN 中有以下几条记录:
PINGMONITORID ,PINGMONITORCNT,RECORDSTATUS,RECORDUPDATETIME,RECORDCREATETIME
A1,1,0, ,
A1,2,0, ,
A1,3,0, ,
A1,4,9, ,
这时候NODE_ADMIN.PingMonitor =1,如果我删除
A1,1,0, ,
那么NODE_ADMIN.PingMonitor还是1
如果我删除
A1,1,0, ,
A1,2,0, ,
A1,3,0, ,
那么NODE_ADMIN.PingMonitor=0

nyfor 2003-4-3 07:47
trigger问题求助 

根据你的trigger 我以为使用表级触发器就可以,使用表级触发不能满足你的要求吗?非得要使用行级触发吗?

kasyunn 2003-4-3 08:34
trigger问题求助 

可以使用表级触发器的,但是我不熟.
愿闻其详.
谢了先.

nyfor 2003-4-3 12:37
trigger问题求助 

把for each row去掉就可以了。

OL.O 2003-4-4 03:19
trigger问题求助 

把fro each row去掉也不一定行,因为它要对本表进行查询,可以编译通过,但在运行时却提示本表已被改变。
对于本贴可采用以下办法。
建立一个对表PINGMONITOR_ADMIN中PingMonitorID的数量的监控表
create table pingmon as select PingMonitorID,count(*) coun
        from PINGMONITOR_ADMIN
        where RecordStatus=0
        group by PingMonitorID;
在表PINGMONITOR_ADMIN上建立两个触发器,一个before 一个after
create or replace trigger PINGMONITOR_ADMIN_BE
before delete on PINGMONITOR_ADMIN
for each row
begin
        update pingmon set coun=coun-1
        where PINGMONITORid=:OLD.PINGMONITORid;
--因为这是个行级触发器,所以每删除一行就会在监控表pingmon上相应coun上减1
if sql%rowcount=0 then
        insert into pingmon values (:OLD.PINGMONITORid,1);
--如果在监控表上无相应的行,就插入一行
end if;
end;

create or replace trigger PINGMONITOR_ADMIN_AF
after delete on PINGMONITOR_ADMIN
for each row
begin
UPDATE NODE_ADMIN a SET PingMonitor =
                (select decode(nvl(coun,0),0,0,1) from pingmon b
                        where a.PINGMONITORid=b.PINGMONITORid(+))
--如果监控表上相应行coun为零,NODE_ADMIN就置0,否则置1
end;
你还要做insert和update的触发器来维护pingmon、NODE_ADMIN和PINGMONITOR_ADMIN的一致性问题,这是关键问题

nyfor 2003-4-4 04:02
trigger问题求助 

[quote][i]原帖由 "OL.O" 发表:[/i]
把fro each row去掉也不一定行,因为它要对本表进行查询,可以编译通过,但在运行时却提示本表已被改变。
[/quote]

really ?

NO!!

OL.O 2003-4-4 04:20
trigger问题求助 

[quote][i]原帖由 "nyfor" 发表:[/i]


really ?

NO!![/quote]
您能用您的方法解决一下最上面的问题吗?请教!!

OL.O 2003-4-4 04:26
trigger问题求助 

还是nyfor说得对,我的方法太复杂了
谢谢nyfor

老鼠(Unix is my 2003-4-4 04:28
trigger问题求助 

[quote][i]原帖由 "kasyunn" 发表:[/i]
下面是一个oracle的trigger,创建的时候没有问题,但是update操作的时候,
出现ORA-04091错误,也就是对行触发器来讲,对当前正在改变的表不允许做select操作,但是如果不用行触发器的话,无法知道当前改变的列的值?.........[/quote]

原始要求是什么?

OL.O 2003-4-4 04:40
trigger问题求助 

[quote][i]原帖由 "kasyunn" 发表:[/i]
下面是一个oracle的trigger,创建的时候没有问题,但是update操作的时候,
出现ORA-04091错误,也就是对行触发器来讲,对当前正在改变的表不允许做select操作,但是如果不用行触发器的话,无法知道当前改变的列的值?.........[/quote]
上面问题中要把 FOR EACH ROW去掉后
就不能引用:OLD和:NEW了

OL.O 2003-4-4 04:53
trigger问题求助 

[quote][i]原帖由 "OL.O" 发表:[/i]
还是nyfor说得对,我的方法太复杂了
谢谢nyfor[/quote]
我刚才做了一下测试,也不一定对,去掉FOR EACH ROW后
不能引用:OLD和:NEW,那最上面的那个触发器就不能编译成功。


还是要谢谢nyfor

kasyunn 2003-4-4 05:35
trigger问题求助 

谢谢.
看来还是得增加一个临时表.

这样应该可以解决了.

nyfor 2003-4-4 07:35
trigger问题求助 

那还不如建立一个view使用instead of 触发器。

trigger问题求助 

经过一番折腾后我想了一个简单而又有效的办法,只用一个语句级触发器就可搞定
CREATE OR REPLACE TRIGGER TRIGGER_PING_MONITOR
AFTER DELETE OR INSERT OR UPDATE OF RecordStatus ON PINGMONITOR_ADMIN
BEGIN
INSERT INTO NODE_ADMIN  SELECT  B.PingMonitorID,1 FROM NODE_ADMIN A,PINGMONITOR_ADMIN B WHERE A.NODEID(+)=B.PingMonitorID AND B.RecordStatus=0 AND A.NODEID IS NULL;
--当插入一个在NODE_ADMIN里没有的行时的处理,如果有相应的行在NODE_ADMIN就无行被插入
UPDATE NODE_ADMIN A SET PINGMONITOR=(SELECT DECODE(COUNT(*),0,0,1) FROM PINGMONITOR_ADMIN B WHERE A.NODEID=B.PingMonitorID AND B.RecordStatus=0);
END TRIGGER_PING_MONITOR;
--每次事务都对NODE_ADMIN进行一次刷新,这样就不存在一致性问题了,前面我的答复虽然也可完成相应的功能,但还要有很多加工,这个就不必改太多了


posted @ 2006-02-08 19:01 青蛙學堂 阅读(466) | 评论 (1)编辑 收藏

CREATE TRIGGER

创建触发器,触发器是一种特殊的存储过程,在用户试图对指定的表执行指定的数据修改语句时自动执行。Microsoft® SQL Server™ 允许为任何给定的 INSERT、UPDATE 或 DELETE 语句创建多个触发器。

语法

CREATE TRIGGER trigger_name
ON { table | view }
[ WITH ENCRYPTION ]
{

    { { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] }
        [ WITH APPEND ]
        [ NOT FOR REPLICATION ]
        AS
        [ { IF UPDATE ( column )
            [ { AND | OR } UPDATE ( column ) ]
                [ ...n ]
        | IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask )
                { comparison_operator } column_bitmask [ ...n ]
        } ]
        sql_statement [ ...n ]
    }
}

参数

trigger_name

是触发器的名称。触发器名称必须符合标识符规则,并且在数据库中必须唯一。可以选择是否指定触发器所有者名称。

Table | view

是在其上执行触发器的表或视图,有时称为触发器表或触发器视图。可以选择是否指定表或视图的所有者名称。

WITH ENCRYPTION

加密 syscomments 表中包含 CREATE TRIGGER 语句文本的条目。使用 WITH ENCRYPTION 可防止将触发器作为 SQL Server 复制的一部分发布。

AFTER

指定触发器只有在触发 SQL 语句中指定的所有操作都已成功执行后才激发。所有的引用级联操作和约束检查也必须成功完成后,才能执行此触发器。

如果仅指定 FOR 关键字,则 AFTER 是默认设置。

不能在视图上定义 AFTER 触发器。

INSTEAD OF

指定执行触发器而不是执行触发 SQL 语句,从而替代触发语句的操作。

在表或视图上,每个 INSERT、UPDATE 或 DELETE 语句最多可以定义一个 INSTEAD OF 触发器。然而,可以在每个具有 INSTEAD OF 触发器的视图上定义视图。

INSTEAD OF 触发器不能在 WITH CHECK OPTION 的可更新视图上定义。如果向指定了 WITH CHECK OPTION 选项的可更新视图添加 INSTEAD OF 触发器,SQL Server 将产生一个错误。用户必须用 ALTER VIEW 删除该选项后才能定义 INSTEAD OF 触发器。

{ [DELETE] [,] [INSERT] [,] [UPDATE] }

是指定在表或视图上执行哪些数据修改语句时将激活触发器的关键字。必须至少指定一个选项。在触发器定义中允许使用以任意顺序组合的这些关键字。如果指定的选项多于一个,需用逗号分隔这些选项。

对于 INSTEAD OF 触发器,不允许在具有 ON DELETE 级联操作引用关系的表上使用 DELETE 选项。同样,也不允许在具有 ON UPDATE 级联操作引用关系的表上使用 UPDATE 选项。

WITH APPEND

指定应该添加现有类型的其它触发器。只有当兼容级别是 65 或更低时,才需要使用该可选子句。如果兼容级别是 70 或更高,则不必使用 WITH APPEND 子句添加现有类型的其它触发器(这是兼容级别设置为 70 或更高的 CREATE TRIGGER 的默认行为)。有关更多信息,请参见 sp_dbcmptlevel

WITH APPEND 不能与 INSTEAD OF 触发器一起使用,或者,如果显式声明 AFTER 触发器,也不能使用该子句。只有当出于向后兼容而指定 FOR 时(没有 INSTEAD OF 或 AFTER),才能使用 WITH APPEND。以后的版本将不支持 WITH APPEND 和 FOR(将被解释为 AFTER)。

NOT FOR REPLICATION

表示当复制进程更改触发器所涉及的表时,不应执行该触发器。

AS

是触发器要执行的操作。

sql_statement

是触发器的条件和操作。触发器条件指定其它准则,以确定 DELETE、INSERT 或 UPDATE 语句是否导致执行触发器操作。

当尝试 DELETE、INSERT 或 UPDATE 操作时,Transact-SQL语句中指定的触发器操作将生效。

触发器可以包含任意数量和种类的 Transact-SQL 语句。触发器旨在根据数据修改语句检查或更改数据;它不应将数据返回给用户。触发器中的 Transact-SQL 语句常常包含控制流语言。CREATE TRIGGER 语句中使用几个特殊的表:

  • deletedinserted 是逻辑(概念)表。这些表在结构上类似于定义触发器的表(也就是在其中尝试用户操作的表);这些表用于保存用户操作可能更改的行的旧值或新值。例如,若要检索 deleted 表中的所有值,请使用:
    SELECT *
    FROM deleted
    
  • 如果兼容级别等于 70,那么在 DELETE、INSERT 或 UPDATE 触发器中,SQL Server 将不允许引用 inserteddeleted 表中的 textntextimage 列。不能访问 inserteddeleted 表中的 textntextimage 值。若要在 INSERT 或 UPDATE 触发器中检索新值,请将 inserted 表与原始更新表联接。当兼容级别是 65 或更低时,对 inserteddeleted 表中允许空值的textntextimage 列,将返回空值;如果这些列不可为空,则返回零长度字符串。

    当兼容级别是 80 或更高时,SQL Server 允许在表或视图上通过 INSTEAD OF 触发器更新 textntextimage 列。

n

是表示触发器中可以包含多条 Transact-SQL 语句的占位符。对于 IF UPDATE (column) 语句,可以通过重复 UPDATE (column) 子句包含多列。

IF UPDATE (column)

测试在指定的列上进行的 INSERT 或 UPDATE 操作,不能用于 DELETE 操作。可以指定多列。因为在 ON 子句中指定了表名,所以在 IF UPDATE 子句中的列名前不要包含表名。若要测试在多个列上进行的 INSERT 或 UPDATE 操作,请在第一个操作后指定单独的 UPDATE(column) 子句。在 INSERT 操作中 IF UPDATE 将返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL) 值。

说明  IF UPDATE (column) 子句的功能等同于 IF、IF...ELSE 或 WHILE 语句,并且可以使用 BEGIN...END 语句块。有关更多信息,请参见控制流语言

 

可以在触发器主体中的任意位置使用 UPDATE (column)。

column

是要测试 INSERT 或 UPDATE 操作的列名。该列可以是 SQL Server 支持的任何数据类型。但是,计算列不能用于该环境中。有关更多信息,请参见数据类型

IF (COLUMNS_UPDATED())

测试是否插入或更新了提及的列,仅用于 INSERT 或 UPDATE 触发器中。COLUMNS_UPDATED 返回 varbinary 位模式,表示插入或更新了表中的哪些列。

COLUMNS_UPDATED 函数以从左到右的顺序返回位,最左边的为最不重要的位。最左边的位表示表中的第一列;向右的下一位表示第二列,依此类推。如果在表上创建的触发器包含 8 列以上,则 COLUMNS_UPDATED 返回多个字节,最左边的为最不重要的字节。在 INSERT 操作中 COLUMNS_UPDATED 将对所有列返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL) 值。

可以在触发器主体中的任意位置使用 COLUMNS_UPDATED。

bitwise_operator

是用于比较运算的位运算符。

updated_bitmask

是整型位掩码,表示实际更新或插入的列。例如,表 t1 包含列 C1C2C3C4C5。假定表 t1 上有 UPDATE 触发器,若要检查列 C2、C3 C4 是否都有更新,指定值 14;若要检查是否只有列 C2 有更新,指定值 2。

comparison_operator

是比较运算符。使用等号 (=) 检查 updated_bitmask 中指定的所有列是否都实际进行了更新。使用大于号 (>) 检查 updated_bitmask 中指定的任一列或某些列是否已更新。

column_bitmask

是要检查的列的整型位掩码,用来检查是否已更新或插入了这些列。

注释

触发器常常用于强制业务规则和数据完整性。SQL Server 通过表创建语句(ALTER TABLE 和 CREATE TABLE)提供声明引用完整性 (DRI);但是 DRI 不提供数据库间的引用完整性。若要强制引用完整性(有关表的主键和外键之间关系的规则),请使用主键和外键约束(ALTER TABLE 和 CREATE TABLE 的 PRIMARY KEY 和 FOREIGN KEY 关键字)。如果触发器表存在约束,则在 INSTEAD OF 触发器执行之后和 AFTER 触发器执行之前检查这些约束。如果违反了约束,则回滚 INSTEAD OF 触发器操作且不执行(激发)AFTER 触发器。

可用 sp_settriggerorder 指定表上第一个和最后一个执行的 AFTER 触发器。在表上只能为每个 INSERT、UPDATE 和 DELETE 操作指定一个第一个执行和一个最后一个执行的 AFTER 触发器。如果同一表上还有其它 AFTER 触发器,则这些触发器将以随机顺序执行。

如果 ALTER TRIGGER 语句更改了第一个或最后一个触发器,则将除去已修改触发器上设置的第一个或最后一个特性,而且必须用 sp_settriggerorder 重置排序值。

只有当触发 SQL 语句(包括所有与更新或删除的对象关联的引用级联操作和约束检查)成功执行后,AFTER 触发器才会执行。AFTER 触发器检查触发语句的运行效果,以及所有由触发语句引起的 UPDATE 和 DELETE 引用级联操作的效果。

触发器限制

CREATE TRIGGER 必须是批处理中的第一条语句,并且只能应用到一个表中。

触发器只能在当前的数据库中创建,不过触发器可以引用当前数据库的外部对象。

如果指定触发器所有者名称以限定触发器,请以相同的方式限定表名。

在同一条 CREATE TRIGGER 语句中,可以为多种用户操作(如 INSERT 和 UPDATE)定义相同的触发器操作。

如果一个表的外键在 DELETE/UPDATE 操作上定义了级联,则不能在该表上定义 INSTEAD OF DELETE/UPDATE 触发器。

在触发器内可以指定任意的 SET 语句。所选择的 SET 选项在触发器执行期间有效,并在触发器执行完后恢复到以前的设置。

与使用存储过程一样,当触发器激发时,将向调用应用程序返回结果。若要避免由于触发器激发而向应用程序返回结果,请不要包含返回结果的 SELECT 语句,也不要包含在触发器中进行变量赋值的语句。包含向用户返回结果的 SELECT 语句或进行变量赋值的语句的触发器需要特殊处理;这些返回的结果必须写入允许修改触发器表的每个应用程序中。如果必须在触发器中进行变量赋值,则应该在触发器的开头使用 SET NOCOUNT 语句以避免返回任何结果集。

DELETE 触发器不能捕获 TRUNCATE TABLE 语句。尽管 TRUNCATE TABLE 语句实际上是没有 WHERE 子句的 DELETE(它删除所有行),但它是无日志记录的,因而不能执行触发器。因为 TRUNCATE TABLE 语句的权限默认授予表所有者且不可转让,所以只有表所有者才需要考虑无意中用 TRUNCATE TABLE 语句规避 DELETE 触发器的问题。

无论有日志记录还是无日志记录,WRITETEXT 语句都不激活触发器。

触发器中不允许以下 Transact-SQL 语句:

ALTER DATABASE CREATE DATABASE DISK INIT
DISK RESIZE DROP DATABASE LOAD DATABASE
LOAD LOG RECONFIGURE RESTORE DATABASE
RESTORE LOG    


 

说明  由于 SQL Server 不支持系统表中的用户定义触发器,因此建议不要在系统表中创建用户定义触发器。

 

多个触发器

SQL Server 允许为每个数据修改事件(DELETE、INSERT 或 UPDATE)创建多个触发器。例如,如果对已有 UPDATE 触发器的表执行 CREATE TRIGGER FOR UPDATE,则将创建另一个更新触发器。在早期版本中,在每个表上,每个数据修改事件(INSERT、UPDATE 或 DELETE)只允许有一个触发器。

说明  如果触发器名称不同,则 CREATE TRIGGER(兼容级别为 70)的默认行为是在现有的触发器中添加其它触发器。如果触发器名称相同,则 SQL Server 返回一条错误信息。但是,如果兼容级别等于或小于 65,则使用 CREATE TRIGGER 语句创建的新触发器将替换同一类型的任何现有触发器,即使触发器名称不同。有关更多信息,请参见 sp_dbcmptlevel

 

递归触发器

当在 sp_dboption 中启用 recursive triggers 设置时,SQL Server 还允许触发器的递归调用。

递归触发器允许发生两种类型的递归:

  • 间接递归

     
  • 直接递归

使用间接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T2。在这种情况下,触发器 T2 将激发并更新 T1

使用直接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T1。由于表 T1 被更新,触发器 TR1 再次激发,依此类推。

下例既使用了间接触发器递归,又使用了直接触发器递归。假定在表 T1 中定义了两个更新触发器 TR1TR2。触发器 TR1 递归地更新表 T1。UPDATE 语句使 TR1TR2 各执行一次。而 TR1 的执行将触发 TR1(递归)和 TR2 的执行。给定触发器的 inserteddeleted 表只包含与唤醒调用触发器的 UPDATE 语句相对应的行。

说明  只有启用 sp_dboptionrecursive triggers 设置,才会发生上述行为。对于为给定事件定义的多个触发器,并没有确定的执行顺序。每个触发器都应是自包含的。

 

禁用 recursive triggers 设置只能禁止直接递归。若要也禁用间接递归,请使用 sp_configurenested triggers 服务器选项设置为 0。

如果任一触发器执行了 ROLLBACK TRANSACTION 语句,则无论嵌套级是多少,都不会进一步执行其它触发器。

嵌套触发器

触发器最多可以嵌套 32 层。如果一个触发器更改了包含另一个触发器的表,则第二个触发器将激活,然后该触发器可以再调用第三个触发器,依此类推。如果链中任意一个触发器引发了无限循环,则会超出嵌套级限制,从而导致取消触发器。若要禁用嵌套触发器,请用 sp_configure nested triggers 选项设置为 0(关闭)。默认配置允许嵌套触发器。如果嵌套触发器是关闭的,则也将禁用递归触发器,与 sp_dboptionrecursive triggers 设置无关。

延迟名称解析

SQL Server 允许 Transact-SQL 存储过程、触发器和批处理引用编译时不存在的表。这种能力称为延迟名称解析。但是,如果 Transact-SQL 存储过程、触发器或批处理引用在存储过程或触发器中定义的表,则只有当兼容级别设置(通过执行 sp_dbcmptlevel 设置)等于 65 时,才会在创建时发出警告。如果使用批处理,则在编译时发出警告。如果引用的表不存在,将在运行时返回错误信息。有关更多信息,请参见延迟名称解析和编译

权限

CREATE TRIGGER 权限默认授予定义触发器的表所有者、sysadmin 固定服务器角色成员以及 db_ownerdb_ddladmin 固定数据库角色成员,并且不可转让。

若要检索表或视图中的数据,用户必须在表或视图中拥有 SELECT 语句权限。若要更新表或视图的内容,用户必须在表或视图中拥有 INSERT、DELETE 和 UPDATE 语句权限。

如果视图中存在 INSTEAD OF 触发器,用户必须在该视图中有 INSERT、DELETE 和 UPDATE 特权,以对该视图发出 INSERT、DELETE 和 UPDATE 语句,而不管实际上是否在视图上执行了这样的操作。

示例
A. 使用带有提醒消息的触发器

当有人试图在 titles 表中添加或更改数据时,下例将向客户端显示一条消息。

说明  消息 50009 是 sysmessages 中的用户定义消息。有关创建用户定义消息的更多信息,请参见 sp_addmessage

 

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'reminder' AND type = 'TR')
   DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE 
AS RAISERROR (50009, 16, 10)
GO
B. 使用带有提醒电子邮件的触发器

titles 表更改时,下例将电子邮件发送给指定的人员 (MaryM)。

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'reminder' AND type = 'TR')
   DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE, DELETE 
AS
   EXEC master..xp_sendmail 'MaryM', 
      'Don''t forget to print a report for the distributors.'
GO
C. 在 employee 和 jobs 表之间使用触发器业务规则

由于 CHECK 约束只能引用定义了列级或表级约束的列,表间的任何约束(在下例中是指业务规则)都必须定义为触发器。

下例创建一个触发器,当插入或更新雇员工作级别 (job_lvls) 时,该触发器检查指定雇员的工作级别(由此决定薪水)是否处于为该工作定义的范围内。若要获得适当的范围,必须引用 jobs 表。

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'employee_insupd' AND type = 'TR')
   DROP TRIGGER employee_insupd
GO
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
AS
/* Get the range of level for this job type from the jobs table. */
DECLARE @min_lvl tinyint,
   @max_lvl tinyint,
   @emp_lvl tinyint,
   @job_id smallint
SELECT @min_lvl = min_lvl, 
   @max_lvl = max_lvl, 
   @emp_lvl = i.job_lvl,
   @job_id = i.job_id
FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id 
   JOIN jobs j ON j.job_id = i.job_id
IF (@job_id = 1) and (@emp_lvl <> 10) 
BEGIN
   RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
   ROLLBACK TRANSACTION
END
ELSE
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
BEGIN
   RAISERROR ('The level for job_id:%d should be between %d and %d.',
      16, 1, @job_id, @min_lvl, @max_lvl)
   ROLLBACK TRANSACTION
END
D. 使用延迟名称解析

下例创建两个触发器以说明延迟名称解析。

USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'trig1' AND type = 'TR')
   DROP TRIGGER trig1
GO
-- Creating a trigger on a nonexistent table.
CREATE TRIGGER trig1
on authors
FOR INSERT, UPDATE, DELETE
AS 
   SELECT a.au_lname, a.au_fname, x.info 
   FROM authors a INNER JOIN does_not_exist x 
      ON a.au_id = x.au_id
GO
-- Here is the statement to actually see the text of the trigger.
SELECT o.id, c.text
FROM sysobjects o INNER JOIN syscomments c 
   ON o.id = c.id
WHERE o.type = 'TR' and o.name = 'trig1'

-- Creating a trigger on an existing table, but with a nonexistent 
-- column.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
      WHERE name = 'trig2' AND type = 'TR')
   DROP TRIGGER trig2
GO
CREATE TRIGGER trig2 
ON authors
FOR INSERT, UPDATE
AS 
   DECLARE @fax varchar(12)
   SELECT @fax = phone   
   FROM authors
GO
-- Here is the statement to actually see the text of the trigger.
SELECT o.id, c.text
FROM sysobjects o INNER JOIN syscomments c 
   ON o.id = c.id
WHERE o.type = 'TR' and o.name = 'trig2'
E. 使用 COLUMNS_UPDATED

下例创建两个表:一个 employeeData 表和一个 auditEmployeeData 表。人力资源部的成员可以修改 employeeData 表,该表包含敏感的雇员薪水信息。如果更改了雇员的社会保险号码 (SSN)、年薪或银行帐户,则生成审核记录并插入到 auditEmployeeData 审核表。

通过使用 COLUMNS_UPDATED() 功能,可以快速测试对这些包含敏感雇员信息的列所做的更改。只有在试图检测对表中的前 8 列所做的更改时,COLUMNS_UPDATED() 才起作用。

USE pubs
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
   WHERE TABLE_NAME = 'employeeData')
   DROP TABLE employeeData
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
   WHERE TABLE_NAME = 'auditEmployeeData')
   DROP TABLE auditEmployeeData
GO
CREATE TABLE employeeData (
   emp_id int NOT NULL,
   emp_bankAccountNumber char (10) NOT NULL,
   emp_salary int NOT NULL,
   emp_SSN char (11) NOT NULL,
   emp_lname nchar (32) NOT NULL,
   emp_fname nchar (32) NOT NULL,
   emp_manager int NOT NULL
   )
GO
CREATE TABLE auditEmployeeData (
   audit_log_id uniqueidentifier DEFAULT NEWID(),
   audit_log_type char (3) NOT NULL,
   audit_emp_id int NOT NULL,
   audit_emp_bankAccountNumber char (10) NULL,
   audit_emp_salary int NULL,
   audit_emp_SSN char (11) NULL,
   audit_user sysname DEFAULT SUSER_SNAME(),
   audit_changed datetime DEFAULT GETDATE()
   )
GO
CREATE TRIGGER updEmployeeData 
ON employeeData 
FOR update AS
/*Check whether columns 2, 3 or 4 has been updated. If any or all of columns 2, 3 or 4 have been changed, create an audit record. The bitmask is: power(2,(2-1))+power(2,(3-1))+power(2,(4-1)) = 14. To check if all columns 2, 3, and 4 are updated, use = 14 in place of >0 (below).*/

   IF (COLUMNS_UPDATED() & 14) > 0
/*Use IF (COLUMNS_UPDATED() & 14) = 14 to see if all of columns 2, 3, and 4 are updated.*/
      BEGIN
-- Audit OLD record.
      INSERT INTO auditEmployeeData
         (audit_log_type,
         audit_emp_id,
         audit_emp_bankAccountNumber,
         audit_emp_salary,
         audit_emp_SSN)
         SELECT 'OLD', 
            del.emp_id,
            del.emp_bankAccountNumber,
            del.emp_salary,
            del.emp_SSN
         FROM deleted del

-- Audit NEW record.
      INSERT INTO auditEmployeeData
         (audit_log_type,
         audit_emp_id,
         audit_emp_bankAccountNumber,
         audit_emp_salary,
         audit_emp_SSN)
         SELECT 'NEW',
            ins.emp_id,
            ins.emp_bankAccountNumber,
            ins.emp_salary,
            ins.emp_SSN
         FROM inserted ins
   END
GO

/*Inserting a new employee does not cause the UPDATE trigger to fire.*/
INSERT INTO employeeData
   VALUES ( 101, 'USA-987-01', 23000, 'R-M53550M', N'Mendel', N'Roland', 32)
GO

/*Updating the employee record for employee number 101 to change the salary to 51000 causes the UPDATE trigger to fire and an audit trail to be produced.*/

UPDATE employeeData
   SET emp_salary = 51000
   WHERE emp_id = 101
GO
SELECT * FROM auditEmployeeData
GO

/*Updating the employee record for employee number 101 to change both the bank account number and social security number (SSN) causes the UPDATE trigger to fire and an audit trail to be produced.*/

UPDATE employeeData
   SET emp_bankAccountNumber = '133146A0', emp_SSN = 'R-M53550M'
   WHERE emp_id = 101
GO
SELECT * FROM auditEmployeeData
GO
F. 使用 COLUMNS_UPDATED 测试 8 列以上

如果必须测试影响到表中前 8 列以外的列的更新时,必须使用 UBSTRING 函数测试由 COLUMNS_UPDATED 返回的适当的位。下例测试影响 Northwind.dbo.Customers 表中的第 3、第 5 或第 9 列的更新。

USE Northwind
DROP TRIGGER  tr1
GO
CREATE TRIGGER tr1 ON Customers
FOR UPDATE AS
   IF ( (SUBSTRING(COLUMNS_UPDATED(),1,1)=power(2,(3-1))
      + power(2,(5-1))) 
      AND (SUBSTRING(COLUMNS_UPDATED(),2,1)=power(2,(1-1)))
      ) 
   PRINT 'Columns 3, 5 and 9 updated'
GO

UPDATE Customers 
   SET ContactName=ContactName,
      Address=Address,
      Country=Country
GO

posted @ 2006-02-08 18:47 青蛙學堂 阅读(2449) | 评论 (3)编辑 收藏

trigger

在同一个表上建三个trigger
ALTER trigger q before update of id
order 1 on DBA.table
for each row  
begin
  insert into a(a,
    b,c) values(\'update\',
    oldnewname.id,newname.id)
end

ALTER trigger c before delete order 1 on
DBA.table
referencing old as oldname
for each row  
begin
  
  insert into a(a,
    b,c) values(\'delete\',\'\',
    oldname.id)
end

ALTER trigger b before insert order 1 on
DBA.table
referencing new as newname
for each row  
begin  
  insert into a(a,
    b,c) values(\'insert\',
    newname.id,\'\')
end
end
执行结果只有两条
a           b       c
delete   123  
insert             321

为什么update 的不执行?太不公平了

posted @ 2006-02-08 18:38 青蛙學堂 阅读(547) | 评论 (2)编辑 收藏

SQL

CREATE  TABLE  NAME1   AS  SELECT   *   FROM   T2

INSERT   INTO   T1   SELECT   *   FROM  T2

posted @ 2006-02-08 12:04 青蛙學堂 阅读(229) | 评论 (1)编辑 收藏

FW : 多线程同步查询

本文将以简单的例子告诉你如何实现线程查询。还等什么,赶快打开Delphi对照着下面的完整源代码试试吧。

Delphi中实现多线程同步查询 优秀的数据库应用应当充分考虑数据库访问的速度问题。通常可以通过优化数据库、优化 查询语句、分页查询等途径收到明显的效果。即使是这样,也不可避免地会在查询时闪现一个带有 SQL符号的沙漏,即鼠标变成了查询等待。最可怜的是用户,他(她)在此时只能无奈地等待。遇到急性子的,干脆在此时尝试 Windows中的其它应用程序,结果致使你的数据库应用显示一大片白色的窗口。真是无奈!

本文将以简单的例子告诉你如何实现线程查询。还等什么,赶快打开Delphi对照着下面的完整源代码试试吧。

在查询时能够做别的事情或者取消查询,这只是基本的线程查询,在你阅读了Delphi有关线程帮助之后能立刻实现。这里介绍的是多个线程查询的同步进行。

在Delphi数据库应用中,都有一个缺省的数据库会话 Session。通常情况下,每个数据库应用中只有这一个会话。无论是查询函数修改数据,在同一时间内只能进行其中的一件事情, 而且进行这一件事情的时候应用程序不能响应键盘、鼠标以及其它的 Windows消息。这就是在 窗口区域会显示一片空白的原因所在。当然,只要将查询或数据操纵构造成线程对象,情况会好一些,至少可以接受窗口消息,也可以随时终止查询或数据操纵,而不会在屏幕上显示出太难看的白色。不过,这只是解决了问题的一部分。假如在进行一个线程查询的时候,用户通过 按钮或菜单又发出了另一个查询的命令,这可如何是好,难道终止正在执行的数据库访问吗? 解决之道就是:多线程同步查询。

实现多线程同步查询的基本思想是,为每一个查询组件(如TQuery组件)创建一个独占的 数据库会话,然后各自进行数据库访问。需要特别注意的是,因为Delphi中的 VCL组件大多都 不是线程安全的,所以应当在线程查询结束后再将DataSource组件与查询组件关联,从而显示 在DBGrid组件中。

下面的例子只实现了静态的线程同步查询,即线程对象是固定的,并随窗体的创建和销毁 而创建和销毁。你可以就此进行改进,为每一个数据查询或数据操纵命令创建一个单独的线程对象,从而达到多线程同步查询的目的。

注意:应用程序中的线程不是越多越好,因为线程将严重吞噬CPU资源,尽管看上去并不明显。谨慎创建和销毁线程将避免你的应用程序导致系统资源崩溃。

下面的例子给出了同时进行的两个线程查询。第一次按下按钮时,线程开始执行;以后每次按下按钮时,如果线程处于挂起状态则继续执行,否则挂起线程;线程执行完毕之后将连接 DataSource,查询结果将显示在相应的DBGrid中。

{ 这里的多线程同步查询演示程序仅包括一个工程文件和一个单元文件 }

{ 窗体中放置的组件有: }

{ 两个Session组件 }

{ 两个Database组件 }

{ 两个Query组件 }

{ 两个DataSource组件 }

{ 两个DBGrid组件 }

{ 一个Button组件 }

{ 除非特别说明,否则上述各组件的属性都取默认值(见各组件注释) }

{ 对于Database组件,就和一般设置一样,有一个正确的连接即可 }

{ 对于Query 组件,需要在各自的属性 SQL中添加一些查询语句,为了 }

{ 看得更清除,建议不要在两个Query 组件中填写相同的查询语句。 }

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Db, DBTables, Grids, DBGrids, StdCtrls;

type
TForm1 = class(TForm)
Session1: TSession; { 属性SessionName填写为S1 }
Database1: TDatabase; { 属性SessionName选择为S1 }
Query1: TQuery;{ 属性Database选择为Database1;属性SessionName选择为S1 }
DataSource1: TDataSource; { 属性DataSet设置为空 }
DBGrid1: TDBGrid; { 属性DataSource选择为DataSource1 }
Session2: TSession; { 属性SessionName填写为S2 }
Database2: TDatabase; { 属性SessionName选择为S2 }
Query2: TQuery;{ 属性Database选择为Database2;属性SessionName选择为S2 }
DataSource2: TDataSource; { 属性DataSet设置为空 }
DBGrid2: TDBGrid; { 属性DataSource选择为DataSource2 }
BtnGoPause: TButton; { 用于执行和挂起线程 }
procedure FormCreate(Sender: TObject); { 创建窗体时创建线程对象 }
procedure FormDestroy(Sender: TObject); { 销毁窗体时销毁线程对象 }
procedure BtnGoPauseClick(Sender: TObject); { 执行线程和挂起线程 }
private
public
end;

TThreadQuery = class(TThread) { 声明线程类 }
private
FQuery: TQuery; { 线程中的查询组件 }
FDataSource: TDataSource; { 与查询组件相关的数据感知组件 }
procedure ConnectDataSource;{ 连接数据查询组件和数据感知组件的方法 }
protected
procedure Execute; override;{ 执行线程的方法 }
public
constructor Create(Query: TQuery;
DataSource: TDataSource); virtual; { 线程构造器 }
end;

var
Form1: TForm1;
Q1, { 线程查询对象1 }
Q2: TThreadQuery; { 线程查询对象2 }

implementation

{$R *.DFM}

{ TThreadQuery类的实现 }

{ 连接数据查询组件和数据感知组件}
procedure TThreadQuery.ConnectDataSource;
begin
FDataSource.DataSet := FQuery;{ 该方法在查询结束后才调用 }
end;

procedure TThreadQuery.Execute;{ 执行线程的方法 }
begin
try
FQuery.Open; { 打开查询 }
Synchronize(ConnectDataSource);{ 线程同步 }
except
ShowMessage('Query Error'); { 线程异常 }
end;
end;

{ 线程查询类的构造器 }
constructor TThreadQuery.Create(Query: TQuery; DataSource: TDataSource);
begin
FQuery := Query;
FDataSource := DataSource;
inherited Create(True);
FreeOnTerminate := False;
end;

{ 创建窗体时创建线程查询对象 }
procedure TForm1.FormCreate(Sender: TObject);
begin
Q1 := TThreadQuery.Create(Query1, DataSource1);
Q2 := TThreadQuery.Create(Query2, DataSource2);
end;

{ 销毁窗体时销毁线程查询对象 }
procedure TForm1.FormDestroy(Sender: TObject);
begin
Q1.Terminate; { 销毁之前终止线程执行 }
Q1.Destroy;
Q2.Terminate; { 销毁之前终止线程执行 }
Q2.Destroy;
end;

{ 开始线程、继续执行线程、挂起线程 }
procedure TForm1.BtnGoPauseClick(Sender: TObject);
begin
if Q1.Suspended then Q1.Resume else Q1.Suspend;
if Q2.Suspended then Q2.Resume else Q2.Suspend;
end;

end.

 

posted @ 2006-02-03 18:20 青蛙學堂 阅读(290) | 评论 (0)编辑 收藏

利用“ Shutdown”命令 实现网络统一关机

笔者所在单位下班后常有很多老师不关电脑,领导发现后要求解决这个问题。我想如果到各办公室去查看,这很费时,于是就想能否通过一台电脑把所有的机器都关掉。因为所有的办公用机安装的都是Windows XP系统,就想到了使用远程关机“Shutdown”命令。具体方法如下:

步骤1 单击“开始→运行”,在对话框中输入“Gpedit.msc”,单击[确定],打开“组策略编辑器”。

步骤2 在“组策略编辑器”窗口的左边打开“计算机配置→Windows设置→安全设置→本地策略→用户权利指派”,在右边的窗口选择“从远端系统强制关机”。在弹出的对话框中显示目前只有“Administrators”组的成员才有权远程关机;单击对话框下方的[添加用户或组]按钮,然后在弹出的对话框中输入“Heyongsheng”(管理员账号),再单击[确定]。

步骤3 这时在“从远端系统强制关机”的属性中便添加了一个“Heyongsheng”用户,单击[确定],最后关闭“组策略编辑器”窗口。

对各办公室的电脑进行上述操作后,我们便给每台计算机的“Heyongsheng”用户授予了远程关机的权限。到下班时,我只要在自己的机器上进行以下操作:

步骤1 单击“开始→运行”,在对话框中输入“Shutdown -I”,屏幕上将显示“远程关机”对话框(图1)。


图1 远程关机控制窗口




步骤2 单击[游览]按钮,出现查找计算机的对话框。

步骤3 单击[开始查找]按钮,会出现本网络中的所有计算机(图2)。


图2 查找局域网中所有计算机

步骤4 按住Ctrl键,用鼠标选择要关闭的计算机,最后单击[确定],这时在远程关机对话框中会出现要关闭的计算机。

步骤5 在选项的下拉框中选择一个合适的关闭理由,然后点击[确定](图3)。


图3 远程关机

依照以上步骤进行操作,远程关机就自动完成了。


posted @ 2006-01-25 13:22 青蛙學堂 阅读(211) | 评论 (0)编辑 收藏

仅列出标题
共43页: First 30 31 32 33 34 35 36 37 38 Last 
<2006年4月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

导航

统计

常用链接

留言簿(8)

随笔分类

随笔档案

收藏夹

青蛙学堂

最新评论

阅读排行榜

评论排行榜