??xml version="1.0" encoding="utf-8" standalone="yes"?>
/*在远E机器操作系l的计算机管理里建立一个用户名为dbbackup的用P密码?234Q同时在那台机器的非pȝ盘里Z个名为backup的共享文件夹Qؓ了安全另外设|这个文件夹只有q个dbbackup用户可以讉K?/
declare @sql varchar(500)
select @sql='\\10.2.0.12\backup\dbname'+'_db_'+convert(varchar(10),getdate(),112) +
substring(convert(varchar(10),getdate(),108),1,2) +'.bak'
exec master..xp_cmdshell 'net use \\10.2.0.12\backup 1234 /user:remotehost\dbbackup'
backup database dbname to disk=@sql --备䆾数据库,q里?0.2.0.12E机器的ipQremotehostE机器的机器名,dbname为本地sqlserver服务器要备䆾的数据库
go
declare @sql varchar(500)
select @sql='del '+'\\10.2.0.12\backup\dbname'+'_db_'+convert(varchar(10),dateadd(day,-7,getdate()),112) +
substring(convert(varchar(10),dateadd(day,-7,getdate()),108),1,2) +'.bak'
exec master..xp_cmdshell @sql --删除7天前的备份,也就是只保留7个最新备?br />
最q我在ؓ公司的框架程序(以数据应用ؓ导向的应用体p)做数据管理模块,q个模块的需求比较简单:备䆾、恢复和清理日志。我公司的Y件基本上以C/S为基本架构,所以数据管理模块中两个主要的功?#8216;备䆾与恢?#8217;都可能会在Client端操作,备䆾与恢?#8217;的文件也都有可能存储在client端,因而这个数据管理模块就必须能够实现在远E备份与恢复数据库?文章的前提阐q完了,p说说如何具体实现吧。其实都很简单,我想写个q程备䆾的测试实?br />
l大家看,p够很清楚的描q吧Q?br />
实例说明Q?br />
环境Qwin2k sqlserver 2K 查询分析?br />
SQLSERVER服务实例名称:mainserver
需要备份的数据库名U? msdb
本地机器名称QClient端)Qdavid
本地用户:zf 密码Q?23
本地域名Qdomain
本地提供备䆾需求的文g?e:\test W一? 建立׃n文g?br />
在程序代码中调用Q或者CMDH口Q?net share test=e:\test
或者用NetShareAddq个API
要说明:
net share : 是WINDOWS内部的网l命令?br />
作用Q徏立本地的׃n资源Q显C当前计机的共享资源信息?字串8
语法Q参?net share /?
W二? 建立׃n信用关系
master..xp_cmdshell 'net use \\david\test 123 /user:domain\zf'
要说明:
1Qxp_cmdshell Q是SQLSERVER的扩展存储过E?br />
作用Q以操作pȝ命o行解释器的方式执行给定的命o字符Ԍ
q以文本行方式返回Q何输出?br />
语法Q参见SQLSERVER联机帮助
2:net use : 是WINDOWS内部的网l命令?br />
作用Q将计算Z׃n资源q接或断开Q或者显C关于计机
q接的信息。该命oq控制持久网l连接?br />
语法Q参?net use /? W三?备䆾数据?br />
backup database msdb to disk='\\david\test\msdb.bak'
q个不需要说明吧Q语法参见SQLSERVER联机帮助 W四? 删除׃n文g?br />
在程序代码中调用Q或者CMDH口Q?net share test /delete
或者用NetShareDelq个API
l果:
已处?1376 ,q些属于数据库 'msdb' 的文?'MSDBData'Q位于文?1 上)?
字串1
已处?1 ,q些属于数据库 'msdb' 的文?'MSDBLog'Q位于文?1 上)?br />
BACKUP DATABASE 操作成功地处理了 1377 ,p?3.653 U(3.086 MB/U)?q样mainserver服务器上的msdb备份到了david机器的E:\test\msdb.bak文g了,使用h很简单吧Q恢复数据库操作也是一P只要第三个步骤的语句改?restore database msdb from disk='\\david\test\msdb.bak'可以啦。。你看完了也可以试试呀Q!Q最单的试工具查询分析?CMDH口Q备注:xp_cmdshell q个扩展存储q程只能SAU别的用戯用,而且是SQLSERVER的安全隐患之一Q许多DBA都喜Ƣ将其删除或者禁用,所以开发h员用时要倍加心哦?br />
文章中的例子只是要的说明了应如何利用扩展存储q程实现q程备䆾与恢复,没有涉及安全以及其他斚w的考虑Q希望读者在代码中自行完善?
go
--Insert Initial Data
--插入初始数据
INSERT INTO tblMaster(details)
SELECT N'A'
UNION ALL
SELECT N'B'
UNION ALL
SELECT N'C'
UNION ALL
SELECT N'?
UNION ALL
SELECT N'?
UNION ALL
SELECT N'?
UNION ALL
SELECT N'?
UNION ALL
SELECT N'A'
UNION ALL
SELECT N'B'
UNION ALL
SELECT N'?
UNION ALL
SELECT N'?
UNION ALL
SELECT N'?
UNION ALL
SELECT N'?
--Copy Table,Exclude Data
--复制表,不拷贝数?/font>
SELECT
TOP 0
*
INTO
tblCopy
FROM
tblMaster
WHERE
1<>1
--Copy data
--拯数据
INSERT INTO
tblCopy(details)
SELECT
details
FROM
tblMaster
--Caculate Distance Between Two Days
--计算两天之间的时间间?br>SELECT datediff(day,'2006-12-12','2007-12-12')
--Search From 2th Record and 6th Record
--查询从第二条记录到第六条记录
SELECT
*
FROM
(
SELECT
top 5 *
FROM (
SELECT
top 6 id,details
FROM tblMaster
ORDER BY
id asc
) a
ORDER BY
id desc
) T
ORDER BY
id asc
--Choose some Records Random
--随机选取几条数据
SELECT
top 3 *
FROM
tblMaster
ORDER BY
newID()
--Delete Duplicated Data
--删除重复数据
DELETE FROM
tblCopy
Where
id not in(
SELECT
min(id)
FROM
tblCopy
Group by
details
)
--if the value of tblMaster.details == null then the following clauses have different values
--如果details列有null?那么如下两条语句得到的返回g{?br>SELECT
count(*)
FROM
tblMaster
SELECT
count(details)
FROM
tblMaster
--Password Encrypt return 1:equel;return 0:not equel
--密码加密 q回1:相等;q回2:不相{?/font>
SELECT pwdcompare('123',pwdencrypt('123'),0)
特别说明Q?/b>
随着你对微Y的SQL服务器数据库实现的逐渐熟悉Q性能优化的需求也进一步增加。徏立一个真正实现最优查询功能的数据库环境的W一步是要懂得SQL服务器系l的优化器是如何工作的?
索引
在创建烦引的时候,SQL服务器系l将自动度量和存储那些与索引列相关的分布状态值相对应的统计信息。这些统计信息常常被优化器用来评估查询的优化{略是否合理? 有两U类型的索引Qclustered索引和non-clustered索引Q根据数据集合的不同Q每U类型的索引都有各自独特的优炏V? clustered索引要求数据表中数据按照序存储。因为数据已l排序,所以对于查找一定范围的索引值时clustered 索引是非常有效的。对于查扑օ有唯一索引值的行信息来_q种cd的烦引性能也优于其他类型的索引? non-clustered索引和教U书中的索引非常怼Q烦引在一个位|而其数据值却在另外一个位|。对于一个数据值的查询搜烦来说Q首先搜索non-clustered的烦引,扑ֈ数据值在数据表中的位|,然后直接从这个位|得到数据。non-clustered 索引对于_匚w查询是非常有用的?
l计?/b>
SQL服务器系l能够维护烦引值的数据l计Ҏ。如果对其进行适当的配|,对于非烦引g能够q行l计度量? 对于性能优化Q数据库理员应该懂得几个基本的l计概念Q这些概늚定义如下Q?
|
UPDATE STATISTICS
在指定的表或索引视图中,对一个或多个l计l(集合Q有关键值分发的信息q行更新。若要基于列生成l计Q请参见 CREATE STATISTICS。?/p>
语法
UPDATE STATISTICS table | view
[
index
| ( statistics_name [ ,...n ] )
]
[ WITH
[
[ FULLSCAN ]
| SAMPLE number { PERCENT | ROWS } ]
| RESAMPLE
]
[ [ , ] [ ALL | COLUMNS | INDEX ]
[ [ , ] NORECOMPUTE ]
]
参数
table | view
要更新统计的表或索引视图的名U。表名和视图名必ȝ合标识符的规则。有x多信息,请参见用标识符。由于烦引名在每个数据库中不唯一Q所以必L定 table 或 view。可选择指定数据库、表或视图所有者。只有在 Microsoft? SQL Server? 2000 企业版中才支持烦引视图?/p>
index
要更新统计的索引。烦引名必须W合标识W的规则。如果未指定 indexQ则更新指定表或索引视图中的所有烦引的分发l计。若要查看烦引名和描q的列表Q请带表名或视图名执行 sp_helpindex?/p>
statistics_name
要更新的l计l(集合Q的名称。统计名U必ȝ合标识符规则。有关生成统计组的更多信息,请参见 CREATE STATISTICS?/p>
n
是表C可以指定多个 statistic_name l的占位W?/p>
FULLSCAN
指定应读取 table 或 view 中的所有行以收集统计。FULLSCAN 提供与 SAMPLE 100 PERCENT 相同的行为。FULLSCAN 不能与 SAMPLE 选项一起用?/p>
SAMPLE number { PERCENT | ROWS }
当ؓ较大的表或视图收集统计时Q指定要采样的表或烦引视囄癑ֈ比或行数。number 只允怋用整敎ͼ无论它是 PERCENT q是 ROWS。若要对较大的表或视图用默认采栯为,请将 SAMPLE number 和 PERCENT 或 ROWS 一起用。Microsoft SQL Server 确保值的采样C低于某一数目Q以保证l计有用。如果 PERCENT、ROWS 或 number 选项D要采L行数q小QSQL Server 则自动根据表或视图中的现有行数改正采栗?/p>
说明 默认行ؓ是在目标表或索引视图上进行采h描。SQL Server 自动计算所需的样本大?/p>
RESAMPLE
指定使用从所有现有统计(包括索引Q承的采样速率来收集统计。如果采样速率D要采L行过,SQL Server 则自动根据表或视图中的现有行数改正采栗?/p>
ALL | COLUMNS | INDEX
指定 UPDATE STATISTICS 语句是否影响列统计、烦引统计或所有现有统计。如果未指定选项Q则 UPDATE STATISTICS 语句影响所有的l计。每个 UPDATE STATISTICS 语句只能指定一U类型(ALL、COLUMNS 或 INDEXQ。?/p>
NORECOMPUTE
指定q期l计不自动重新计。统计过期与否取决于在烦引列上进行的 INSERT、UPDATE 和 DELETE 操作的数量。指定该选项Ӟ导臾bSQL Server 用自动l计重徏功能。若要还原自动统计重新计,请重新执行 UPDATE STATISTICSQ不要 NORECOMPUTE 选项Q,或者执行 sp_autostats?/p>
重要 用自动l计重新计算会导臾bSQL Server 查询优化器对于涉及指定表的查询选择非最佳的{略?/p>
注释
SQL Server 保留每个索引中关于键值分发的l计Qƈ且用这些统计来军_查询处理中用哪个(或哪些)索引。用户可以通过使用 CREATE STATISTICS 语句生成Z非烦引列的统计。查询优化依赖于分发步骤的准性:
如果索引中的键值有显著变化Q请Ҏ索引重新q行 UPDATE STATISTICS?/p>
如果索引列中d、更Ҏ删除大量数据Q即如果键值分发更改)Q或者用 TRUNCATE TABLE 语句表截断然后重新填充Q请使用 UPDATE STATISTICS。?br />若要查看l计最q一ơ更新的旉Q请使用 STATS_DATE 函数?/p>
只有当能够在计算列上创徏索引Ӟ才可以在包含q些计算列的表上创徏或更新统计。有兛_计算列上创徏索引的要求和限制的更多信息,请参见 CREATE INDEX?/p>
权限
UPDATE STATISTICS 权限默认授予表或视图的所有者,q且该权限不可{让?/p>
CZ
A. 更新单个表的所有统?br />本示例更新表 authors 上的所有烦引分发统计?/p>
UPDATE STATISTICS authors
B. 仅更新单一索引的统?br />本示例仅更新表 authors 的烦引 au_id_ind 的分发信息。?/p>
UPDATE STATISTICS authors au_id_ind
C. 使用 50% 采样更新特定l计l(集合Q的l计
本示例首先创 authors 中 au_lname 列和 au_fname 列的l计l,然后对其q行更新?/p>
CREATE STATISTICS anames
ON authors (au_lname, au_fname)
WITH SAMPLE 50 PERCENT
GO
-- Time passes. The UPDATE STATISTICS statement is then executed.
UPDATE STATISTICS authors(anames)
WITH SAMPLE 50 PERCENT
GO
D. 使用 FULLSCAN 和 NORECOMPUTE 更新特定l计l(集合Q的l计
本示例更新表 authors 中的 anames l计l(集合Q,强制对表 authors 中的所有行q行完全扫描Qƈ且关闭该l计l(集合Q的自动l计更新?/p>
UPDATE STATISTICS authors(anames)
WITH FULLSCAN, NORECOMPUTE
sp_updatestats对当前数据库中所有用户定义的表运行 UPDATE STATISTICS?/p>
语法
sp_updatestats [[@resample =] ''resample'']
q回代码?br />0Q成功)或?Q失败)
参数
[@resample =] ''resample''
指定 sp_updatestats 用 UPDATE STATISTICS 命o的 RESAMPLE 选项。新l计表将l承旧统计表的采h率。如果未指定 ''resample''Q则 sp_updatestats 使用默认采样更新l计表。该参数的数据类型ؓ varchar(8)Q默认gؓ ''NO''?/p>
注释
sp_updatestats 会显CC其q度的消息。完成更C后,该存储过E将报告已ؓ所有的表更Cl计信息。?/p>
权限
只有 DBO 和 sysadmin 固定服务器角色的成员才能执行该过E?/p>
CZ
下例为数据库 pubs 中的表更新统计信息?/p>
USE pubs
EXEC sp_updatestats
Sqlserver7 ~程技术内q提供的Ҏ.
drop proc pr_updateindex
create proc pr_updateindex
as
set nocount on
declare get_index_curs cursor
for select name--tablename
from sysobjects --systemtable
where type=''u'' -usertable
declare @holdtable varchar(30)
declare @message varchar(40)
declare @dynamic varchar(51)
open getindex_curs
fetch next from getindex_curs into @holdtable
while @@fetch_status=0
begin
select @dynamic=''update statistics ''+@holdtable
select @message=''updating''+@holdtable
exec(@dynamic)
print @message
fetch next from getindex_curs into @holdtable
end
close getindex_curs
Copyright (C) 2003 Cameron Michelis copying and redistribution of this file is permitted provided
this notice and the above comments are preserved.
*/
Set quoted_identifier off
use master
DECLARE @fillfactor varchar(2)
DECLARE @tablename varchar(30)
DECLARE @tablename_header varchar(75)
DECLARE @dataname varchar(30)
DECLARE @dataname_header varchar(75)
DECLARE datanames_cursor CURSOR FOR SELECT name FROM sysdatabases
WHERE name not in ('master', 'pubs', 'tempdb', 'model', 'northwind')
/* Variable Initialization */
select @fillfactor = "0" -- Set Fill factor here
-- Note "0" will use original fillfactor.
/* End Variable Initialization */
OPEN datanames_cursor
FETCH NEXT FROM datanames_cursor INTO @dataname
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status = -2)
BEGIN
FETCH NEXT FROM datanames_cursor INTO @dataname
CONTINUE
END
SELECT @dataname_header = "Database " + RTRIM(UPPER(@dataname))
PRINT " "
PRINT @dataname_header
PRINT " "
EXEC ("USE " + @dataname + " DECLARE tnames_cursor CURSOR FOR SELECT name from sysobjects where type = 'U'")
Select @dataname_header = RTRIM(UPPER(@dataname))
Exec ("Use " + @dataname)
OPEN tnames_cursor
FETCH NEXT FROM tnames_cursor INTO @tablename
WHILE (@@fetch_status <> -1)
BEGIN
IF (@@fetch_status = -2)
BEGIN
FETCH NEXT FROM tnames_cursor INTO @tablename
CONTINUE
END
SELECT @tablename_header = " Updating " + RTRIM(UPPER(@tablename))
PRINT ""
PRINT @tablename_header
EXEC ("USE " + @dataname + " DBCC DBREINDEX (" + @tablename + "," + "''" + "," + @fillfactor + ")")
EXEC ("USE " + @dataname + " UPDATE STATISTICS " + @tablename)
FETCH NEXT FROM tnames_cursor INTO @tablename
END
DEALLOCATE tnames_cursor
FETCH NEXT FROM datanames_cursor INTO @dataname
END
DEALLOCATE datanames_cursor
PRINT ""
PRINT " "
PRINT "Indexing complete for All User Databases"
SET QUOTED_IDENTIFIER OFF /* Start with master DB */ USE master /* Create Variables */ DECLARE @DBName CHAR(64) DECLARE @TableName CHAR(64) DECLARE @FQTableName CHAR(64) DECLARE @TempVar CHAR(256) /* Create DB List */ DECLARE DBCursor CURSOR FOR SELECT name FROM master..sysdatabases OPEN DBCursor FETCH NEXT FROM DBCursor INTO @DBName /* Create Database Loop */ WHILE @@FETCH_STATUS = 0 BEGIN /* Retrieve Table List */ PRINT 'Retrieving Table List for DB ' + @DBName EXEC ('SELECT name AS TableName INTO ##TableNames FROM [' + @DBName + ']..sysobjects WHERE type = ''U''') /* Open Table List */ DECLARE TableCursor CURSOR FOR SELECT TableName FROM ##TableNames OPEN TableCursor FETCH NEXT FROM TableCursor INTO @TableName /* Create Table Loop */ WHILE @@FETCH_STATUS = 0 BEGIN /* Add DB Name to Table Name */ SELECT @FQTableName = QUOTENAME(RTRIM(@DBName)) + '..' + QUOTENAME(RTRIM(@TableName)) SELECT @TableName = RTRIM(@DBName) + '..' + RTRIM(@TableName) /* ReIndex Table */ PRINT 'ReIndexing Table ' + @TableName DBCC DBREINDEX(@TableName) /* Update Statics on Table */ PRINT 'Updating Statistics on Table ' + @TableName EXEC ('UPDATE STATISTICS ' + @FQTableName) /* Get Next Table Name */ FETCH NEXT FROM TableCursor INTO @TableName END /* Close Table Cursor */ CLOSE TableCursor DEALLOCATE TableCursor /* Remove Tempory Table */ DROP TABLE ##TableNames /* Preform DB Checks */ PRINT 'Preforming DB Checks on ' + @DBName DBCC CHECKDB (@DBName) /* Get Next Table Name */ FETCH NEXT FROM DBCursor INTO @DBName END /* Close DB Curosor */ CLOSE DBCursor DEALLOCATE DBCursor /* Finished */
在几千条记录?存在着些相同的记录,如何能用SQL语句,删除掉重复的?
1、查找表中多余的重复记录Q重复记录是Ҏ单个字段QpeopleIdQ来判断
select * from people
where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1)
2、删除表中多余的重复记录Q重复记录是Ҏ单个字段QpeopleIdQ来判断Q只留有rowid最的记录
delete from people
where peopleId in (select peopleId from people group by peopleId having count(peopleId) > 1)
and rowid not in (select min(rowid) from people group by peopleId having count(peopleId )>1)
3、查找表中多余的重复记录Q多个字D)
select * from vitae a
where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1)
4、删除表中多余的重复记录Q多个字D)Q只留有rowid最的记录
delete from vitae a
where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1) and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)>1)
5、查找表中多余的重复记录Q多个字D)Q不包含rowid最的记录
select * from vitae a
where (a.peopleId,a.seq) in (select peopleId,seq from vitae group by peopleId,seq having count(*) > 1) and rowid not in (select min(rowid) from vitae group by peopleId,seq having count(*)>1)
也可以这?
CREATE TABLE tb1(ID int, 名称 NVARCHAR(30), 备注 NVARCHAR(1000))
INSERT TB1 (ID,名称,备注)VALUES(1,'DDD',1)
INSERT TB1 (ID,名称,备注)VALUES(1,'5100','D')
INSERT TB1 (ID,名称,备注)VALUES(1,'5200','E')
_________________________________
上面两种Ҏ,哪种Ҏ效率?
(2)赋值时:
SELECT @a=N'aa'
SET @a=N'aa'
_________________________________
上面两种Ҏ,哪种Ҏ效率?
(3)取前几条数据?br />set ROWCOUNT 2 select * from tb order by fd
select Top 2 * from tb order by fd
_________________________________
上面两种Ҏ,哪种Ҏ效率?
(4)条g判断?br /> where 0<(select count(*) from tb where ……)
where exists(select * from tb where ……)
_________________________________
上面两种Ҏ,哪种Ҏ效率?
(5)NULLIF的?---->同理它的反函数ISNULL的?br />update tb set fd=case when fd=1 then null else fd end
update tb set fd=nullif(fd,1)
_________________________________
上面两种Ҏ,哪种Ҏ效率?
Q?Q从字符串中取子字符串时
substring('abcdefg',1,3)
left('abcderg',3)_
________________________________
上面两种Ҏ,哪种Ҏ效率?
(7)EXCEPT和Not in的区?
(8)INTERSECT和UNION的区?
(1)一ơ插入多条数据时:
W?U好一? 但也得有? 因ؓW?U的union all是做Z个语句整? 查询优化器会试做优? 同时, 也要先算个结果再插入?
2. 如果是单个赋? 没有什么好比较的话.
不过, 如果是ؓ多个变量赋? 我测试过, SELECT 一ơ性赋? 比用SET 逐个赋值效率好.
3. SET ROWCOUNT和TOP 是一L, 包括执行的计划等都是一L
4. q个一般是exists? 当然, 具体q要看你后面的子查询的条? 是否会引用外层查询中的对象的?
exists查到有值就q回, 而且不返回结果集, count需要统计出所有满x件的, 再返回一个结果集, 所以一般情况下exists?
5. 应该是一L
6. 基本上是一L
7. except会去重复, not in 不会(除非你在select中显式指?
except用于比较的列是所有列, 除非写子查询限制? not in 没有q种情况
8. intersect是两个查询都有的非重复?交集), union是两个查询结果的所有不重复?q)
/***************************************************************************************************************/
CREATE FUNCTION [dbo].[f_num_eng] (@num numeric(15,2))
RETURNS varchar(400) WITH ENCRYPTION
AS
BEGIN
--All rights reserved. pbsql
DECLARE @i int,@hundreds int,@tenth int,@one int
DECLARE @thousand int,@million int,@billion int
DECLARE @numbers varchar(400),@s varchar(15),@result varchar(400)
SET @numbers='one two three four five '
+'six seven eight nine ten '
+'eleven twelve thirteen fourteen fifteen '
+'sixteen seventeen eighteen nineteen '
+'twenty thirty forty fifty '
+'sixty seventy eighty ninety '
SET @s=RIGHT('000000000000000'+CAST(@num AS varchar(15)),15)
SET @billion=CAST(SUBSTRING(@s,1,3) AS int)--?12位整?分成4D:?、百万、千、百?
SET @million=CAST(SUBSTRING(@s,4,3) AS int)
SET @thousand=CAST(SUBSTRING(@s,7,3) AS int)
SET @result=''
SET @i=0
WHILE @i<=3
BEGIN
SET @hundreds=CAST(SUBSTRING(@s,@i*3+1,1) AS int)--百位0-9
SET @tenth=CAST(SUBSTRING(@s,@i*3+2,1) AS int)
SET @one=(CASE @tenth WHEN 1 THEN 10 ELSE 0 END)+CAST(SUBSTRING(@s,@i*3+3,1) AS int)--??-19
SET @tenth=(CASE WHEN @tenth<=1 THEN 0 ELSE @tenth END)--十位0?-9
IF (@i=1 and @billion>0 and (@million>0 or @thousand>0 or @hundreds>0)) or
(@i=2 and (@billion>0 or @million>0) and (@thousand>0 or @hundreds>0)) or
(@i=3 and (@billion>0 or @million>0 or @thousand>0) and (@hundreds>0))
SET @result=@result+', '--百位不是0?每段??接符,
IF (@i=3 and (@billion>0 or @million>0 or @thousand>0) and (@hundreds=0 and (@tenth>0 or @one>0)))
SET @result=@result+' and '--百位???接符AND
IF @hundreds>0
SET @result=@result+RTRIM(SUBSTRING(@numbers,@hundreds*10-9,10))+' hundred'
IF @tenth>=2 and @tenth<=9
BEGIN
IF @hundreds>0
SET @result=@result+' and '
SET @result=@result+RTRIM(SUBSTRING(@numbers,@tenth*10+171,10))
END
IF @one>=1 and @one<=19
BEGIN
IF @tenth>0
SET @result=@result+'-'
ELSE
IF @hundreds>0
SET @result=@result+' and '
SET @result=@result+RTRIM(SUBSTRING(@numbers,@one*10-9,10))
END
IF @i=0 and @billion>0
SET @result=@result+' billion'
IF @i=1 and @million>0
SET @result=@result+' million'
IF @i=2 and @thousand>0
SET @result=@result+' thousand'
SET @i=@i+1
END
IF SUBSTRING(@s,14,2)<>'00'
BEGIN
SET @result=@result+' point '
IF SUBSTRING(@s,14,1)='0'
SET @result=@result+'zero'
ELSE
SET @result=@result+RTRIM(SUBSTRING(@numbers,CAST(SUBSTRING(@s,14,1) AS int)*10-9,10))
IF SUBSTRING(@s,15,1)<>'0'
SET @result=@result+' '+RTRIM(SUBSTRING(@numbers,CAST(SUBSTRING(@s,15,1) AS int)*10-9,10))
END
RETURN(@result)
END
一?概述
在应用系l中Q数据库往往是最核心的部分,一旦数据库毁坏或损坏,会带来巨大的损失,所以数据库的管理越来越重要。我们在做数据库理与维护工作中Q不可避免会出现各种各样的错误,本文针对数据库的日志文g丢失时如何利用MDF文g恢复数据库的Ҏq行了研I?
二?数据库的恢复
当数据库的主数据MDF文g完好无损Ӟ在丢׃LDF文g的情况下Q如何利用MDF文g恢复数据库?我们把SQL Server的日志文件分Zc:一cL无活动事务的日志Q另一cL含活动事务的日志Q根据不同的日志Q采取不同的Ҏ来恢复数据库?br />
1. 无活动事务的日志恢复
无活动事务的日志丢失Ӟ我们很容易利用MDF文g直接恢复数据库,具体Ҏ如下Q?br />
?分离被质疑的数据库,可用企业理器中?分离数据库工?Q或者用存储q程sp_detach_db分离数据库;
②利用MDF文g附加数据库生成新的日志文Ӟ可用企业理器中?附加数据?的工P或者用存储q程sp_attach_single_file_db附加数据库?br />
如果数据库的日志文g中含有活动事务,利用此方法就不能恢复数据库?br />
2. 含活动事务的日志恢复
含有zd事务的日志丢失时Q利用上q方法就会出?数据库和日志文g不符合,不能附加数据?。对于这U情况下Q我们采用如下方法:
①新建同名数据库AAAQƈ讑֮为紧急模?br />
·停止SQL Server服务器;
·把数据库L据MDF文gU走Q?br />
·启SQL Server服务器,新徏一个同名的数据库AAAQ?
·停止SQL Server服务器,把移走的MDF文g再覆盖回来;
·启动SQL Server服务器,把AAA设ؓ紧急模式,不过默认情况下,pȝ表是不能随便修改的,必须首先讄一下其能被修改,q行以下语句卛_Q?br />
Use Master Go sp_configure ’allow updates?1 reconfigure with override Go |
update sysdatabases set status=32768 where hame=’AAA?/td> |
Sp_dboption ’AAA? ’single user? ’true?/td> |
DBCC CHECKDB(’AAA? |
update sysdatabases set status=28 where name=’AAA?br />sp_configure ’allow updates?0 reconfigure with override Go |
以下单说明恢复数据方法:
1Q如果误操作之前存在一个全库备份(或已有多个差异备份或增量备䆾Q,首先要做的事是q?br />q行一ơ日志备份(如果Z不让日志文g变大而置trunc. log on chkpt.选项?那你死翘了)
backup log dbName to disk='fileName'
2Q恢复一个全库备份,注意需要用with norecoveryQ如果还有其他差异或增量备䆾Q则逐个?br />?br />restore database dbName from disk='fileName' with norecovery
3Q恢复最后一个日志备份即刚做的日志备份,指定恢复旉点到误操作之前的时刻
restore log dbName from disk='fileName'
with stopat='date_time'
以上q些操作都可以在SQL SERVER企业理器里完成Q难度不大。。?/p>
当然Q如果误操作是一些不记日志的操作比如truncate tableQselect into{操作,那么是无法利
用上q方法来恢复数据?..
也称聚类索引、簇集烦引)和非聚集索引Qnonclustered indexQ也U非聚类索引、非集索引Q。下面,我们举例?/p>
说明一下聚集烦引和非聚集烦引的区别Q?/p>
其实Q我们的汉语字典的正文本w就是一个聚集烦引。比如,我们要查“安”字Q就会很自然地翻开字典的前几页
Q因为“安”的拼音是“an”,而按照拼x序汉字的字典是以英文字母“a”开头ƈ以“z”结Q那么“安”字?/p>
自然地排在字典的前部。如果您d了所有以“a”开头的部分仍然找不到这个字Q那么就说明您的字典中没有这个字Q?/p>
同样的,如果查“张”字Q那您也会将您的字典d最后部分,因ؓ“张”的拼音是“zhang”。也是_字典的正?/p>
部分本n是一个目录,您不需要再L其他目录来找到您需要找的内宏V我们把q种正文内容本n是一U按照一?/p>
规则排列的目录称为“聚集烦引”?/p>
如果您认识某个字Q您可以快速地从自动中查到q个字。但您也可能会遇到您不认识的字,不知道它的发韻Iq时
候,您就不能按照刚才的方法找到您要查的字Q而需要去Ҏ“偏旁部首”查到您要找的字Q然后根据这个字后的늠
直接d某页来找到您要找的字。但您结合“部首目录”和“检字表”而查到的字的排序q不是真正的正文的排序方?/p>
Q比如您查“张”字Q我们可以看到在查部首之后的字表中“张”的늠?72,字表中“张”的上面是“驰”字
Q但늠却是63,“张”的下面是“徃”字Q页面是390c很昄Q这些字q不是真正的分别位于“张”字的上下方
Q现在您看到的连l的“驰、张、徃”三字实际上是他们在非聚集索引中的排序Q是字典正文中的字在非聚集烦引中
的映。我们可以通过q种方式来找到您所需要的字,但它需要两个过E,先找到目录中的结果,然后再翻到您所需?/p>
的页码。我们把q种目录Ua是目录,正文Ua是正文的排序方式UCؓ“非聚集索引”?/p>
通过以上例子Q我们可以理解到什么是“聚集烦引”和“非聚集索引”。进一步引申一下,我们可以很容易的理解
Q每个表只能有一个聚集烦引,因ؓ目录只能按照一U方法进行排序?/p>
二、何时用聚集烦引或非聚集烦?/p>
下面的表ȝ了何时用聚集烦引或非聚集烦引(很重要)Q?
动作描述 使用聚集索引 使用非聚集烦?
列经常被分组排序 ??
q回某范围内的数??不应
一个或极少不同?不应 不应
数目的不同??不应
大数目的不同?不应 ?
频繁更新的列 不应 ?
外键???
主键???
频繁修改索引?不应 ?
事实上,我们可以通过前面聚集索引和非聚集索引的定义的例子来理解上表。如Q返回某范围内的数据一V比?/p>
您的某个表有一个时间列Q恰好您把聚合烦引徏立在了该列,q时您查?004q??日至2004q?0?日之间的全部数据
Ӟq个速度将是很快的Q因为您的这本字典正文是按日期进行排序的Q聚cȝ引只需要找到要索的所有数据中?/p>
开头和l尾数据卛_Q而不像非聚集索引Q必d查到目录中查到每一Ҏ据对应的늠Q然后再Ҏ늠查到具体?/p>
宏V?/p>
三、结合实际,谈烦引用的误区
理论的目的是应用。虽然我们刚才列Z何时应用聚集烦引或非聚集烦引,但在实践中以上规则却很容易被忽视
或不能根据实际情况进行综合分析。下面我们将Ҏ在实践中遇到的实际问题来谈一下烦引用的误区Q以便于大家?/p>
握烦引徏立的Ҏ?/p>
1、主键就是聚集烦?/p>
q种xW者认为是极端错误的,是对聚集索引的一U浪贏V虽然SQL SERVER默认是在主键上徏立聚集烦引的?/p>
通常Q我们会在每个表中都建立一个ID列,以区分每条数据,q且q个ID列是自动增大的,步长一般ؓ1。我们的q?/p>
个办公自动化的实例中的列Gid是如此。此Ӟ如果我们这个列设ؓ主键QSQL SERVER会将此列默认集烦引。这
样做有好处,是可以让您的数据在数据库中按照IDq行物理排序Q但W者认样做意义不大?/p>
显而易见,聚集索引的优势是很明昄Q而每个表中只能有一个聚集烦引的规则Q这使得聚集索引变得更加珍贵?/p>
从我们前面谈到的聚集索引的定义我们可以看出,使用聚集索引的最大好处就是能够根据查询要求,q速羃查?/p>
范围Q避免全表扫描。在实际应用中,因ؓIDh自动生成的,我们q不知道每条记录的IDP所以我们很隑֜实践?/p>
用IDhq行查询。这׃让ID可个主键作集烦引成ZU资源浪贏V其ơ,让每个ID号都不同的字D作?/p>
索引也不W合“大数目的不同值情况下不应建立聚合索引”规则;当然Q这U情况只是针对用L怿改记录内容,?/p>
别是索引的时候会负作用,但对于查询速度q没有媄响?/p>
在办公自动化pȝ中,无论是系l首|C的需要用L收的文g、会议还是用戯行文件查询等M情况下进?/p>
数据查询都离不开字段的是“日期”还有用hw的“用户名”?/p>
通常Q办公自动化的首会昄每个用户未{收的文件或会议。虽然我们的where语句可以仅仅限制当前用户未
{收的情况,但如果您的系l已建立了很长时_q且数据量很大,那么Q每ơ每个用h开首页的时候都q行一ơ全
表扫描,q样做意义是不大的,l大多数的用?个月前的文g都已l浏览过了,q样做只能徒增数据库的开销而已。事
实上Q我们完全可以让用户打开pȝ首页Ӟ数据库仅仅查询这个用戯3个月来未阅览的文Ӟ通过“日期”这个字D?/p>
来限制表扫描Q提高查询速度。如果您的办公自动化pȝ已经建立?q_那么您的首页昄速度理论上将是原来速度8
倍,甚至更快?/p>
在这里之所以提到“理Z”三字,是因为如果您的聚集烦引还是盲目地建在IDq个主键上时Q您的查询速度是没
有这么高的,即您在“日期”这个字D上建立的烦引(非聚合烦引)。下面我们就来看一下在1000万条数据量的情况
下各U查询的速度表现Q?个月内的数据?5万条Q:
Q?Q仅在主键上建立聚集索引Qƈ且不划分旉D:
Select gid,fariqi,neibuyonghu,title from tgongwen 用时Q?28470毫秒Q即Q?28U)
Q?Q在主键上徏立聚集烦引,在fariq上徏立非聚集索引Q?/p>
select gid,fariqi,neibuyonghu,title from Tgongwen
where fariqi> dateadd(day,-90,getdate()) 用时Q?3763毫秒Q?4U)
Q?Q将聚合索引建立在日期列QfariqiQ上Q?/p>
select gid,fariqi,neibuyonghu,title from Tgongwen
where fariqi> dateadd(day,-90,getdate()) 用时Q?423毫秒Q?U)
虽然每条语句提取出来的都?5万条数据Q各U情늚差异却是巨大的,特别是将聚集索引建立在日期列时的差异
。事实上Q如果您的数据库真的?000万容量的话,把主键徏立在ID列上Q就像以上的W??U情况,在网上的表?/p>
是时Q根本就无法昄。这也是我摒弃ID列作集烦引的一个最重要的因素。得Z上速度的方法是Q在各个
select语句前加Q?/p>
declare @d datetime
set @d=getdate() q在select语句后加Q?/p>
select [语句执行p旉(毫秒)]=datediff(ms,@d,getdate()) 2、只要徏立烦引就能显著提高查询速度
事实上,我们可以发现上面的例子中Q第2?条语句完全相同,且徏立烦引的字段也相同;不同的仅是前者在
fariqi字段上徏立的是非聚合索引Q后者在此字D上建立的是聚合索引Q但查询速度却有着天壤之别。所以,q是在
M字段上简单地建立索引p提高查询速度?/p>
从徏表的语句中,我们可以看到q个有着1000万数据的表中fariqi字段?003个不同记录。在此字D上建立聚合?/p>
引是再合适不q了。在现实中,我们每天都会发几个文Ӟq几个文件的发文日期q同,q完全符合徏立聚集烦引要
求的Q“既不能l大多数都相同,又不能只有极数相同”的规则。由此看来,我们建立“适当”的聚合索引对于我们
提高查询速度是非帔R要的?/p>
3、把所有需要提高查询速度的字D都加进聚集索引Q以提高查询速度
上面已经谈到Q在q行数据查询旉M开字段的是“日期”还有用hw的“用户名”。既然这两个字段都是?/p>
此的重要Q我们可以把他们合ƈhQ徏立一个复合烦引(compound indexQ?/p>
很多为只要把M字段加进聚集索引Q就能提高查询速度Q也有h感到qhQ如果把复合的聚集烦引字D分开
查询Q那么查询速度会减慢吗Q带着q个问题Q我们来看一下以下的查询速度Q结果集都是25万条数据Q:Q日期列
fariqi首先排在复合聚集索引的v始列Q用户名neibuyonghu排在后列Q:
Q?Qselect gid,fariqi,neibuyonghu,title from Tgongwen where fariqi>''2004-5-5'' 查询速度Q?513?/p>
U?/p>
Q?Qselect gid,fariqi,neibuyonghu,title from Tgongwen
where fariqi>''2004-5-5'' and neibuyonghu=''办公?' 查询速度Q?516毫秒
Q?Qselect gid,fariqi,neibuyonghu,title from Tgongwen where neibuyonghu=''办公?' 查询速度Q?/p>
60280毫秒
从以上试验中Q我们可以看到如果仅用聚集烦引的起始列作为查询条件和同时用到复合聚集索引的全部列的查询?/p>
度是几乎一LQ甚x用上全部的复合烦引列q要略快Q在查询l果集数目一L情况下)Q而如果仅用复合聚集烦
引的非v始列作ؓ查询条g的话Q这个烦引是不vM作用的。当Ӟ语句1?的查询速度一h因ؓ查询的条目数一
P如果复合索引的所有列都用上,而且查询l果的话,q样׃形成“烦引覆盖”,因而性能可以辑ֈ最优。同?/p>
Q请CQ无论您是否l常使用聚合索引的其他列Q但其前导列一定要是用最频繁的列?/p>
四、其他书上没有的索引使用l验ȝ
1、用聚合索引比用不是聚合索引的主键速度?/p>
下面是实例语句:Q都是提?5万条数据Q?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16'' 使用旉Q?326毫秒
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid<=250000 使用旉Q?470毫秒
q里Q用聚合索引比用不是聚合索引的主键速度快了q?/4?/p>
2、用聚合索引比用一般的主键作order by旉度快,特别是在数据量情况?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by fariqi 用时Q?2936
select gid,fariqi,neibuyonghu,reader,title from Tgongwen order by gid 用时Q?8843
q里Q用聚合索引比用一般的主键作order byӞ速度快了3/10。事实上Q如果数据量很小的话Q用聚集索引作ؓ
排序列要比用非聚集索引速度快得明显的多Q而数据量如果很大的话Q如10万以上,则二者的速度差别不明显?/p>
3、用聚合烦引内的时间段Q搜索时间会按数据占整个数据表的癑ֈ比成比例减少Q而无合烦引用了多少?/p>
Q?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>''2004-1-1'' 用时Q?343毫秒Q提
?00万条Q?
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi>''2004-6-6'' 用时Q?170毫秒Q提
?0万条Q?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16'' 用时Q?326毫秒Q和
上句的结果一模一栗如果采集的数量一P那么用大于号和等于号是一LQ?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen
where fariqi>''2004-1-1'' and fariqi<''2004-6-6'' 用时Q?280毫秒
4、日期列不会因ؓ有分U的输入而减慢查询速度
下面的例子中Q共?00万条数据Q?004q??日以后的数据?0万条Q但只有两个不同的日期,日期_到日Q之
前有数据50万条Q有5000个不同的日期Q日期精到U?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen
where fariqi>''2004-1-1'' order by fariqi 用时Q?390毫秒
select gid,fariqi,neibuyonghu,reader,title from Tgongwen
where fariqi<''2004-1-1'' order by fariqi 用时Q?453毫秒
五、其他注意事?/p>
“水可蝲舟,亦可覆舟”,索引也一栗烦引有助于提高索性能Q但q多或不当的索引也会Dpȝ低效。因?/p>
用户在表中每加进一个烦引,数据库就要做更多的工作。过多的索引甚至会导致烦引碎片?/p>
所以说Q我们要建立一个“适当”的索引体系Q特别是对聚合烦引的创徏Q更应精益求_,以您的数据库能得到
高性能的发挥?/p>
当然Q在实践中,作ؓ一个尽职的数据库管理员Q您q要多测试一些方案,扑և哪种Ҏ效率最高、最为有效?/p>
改善SQL语句
很多Z知道SQL语句在SQL SERVER中是如何执行的,他们担心自己所写的SQL语句会被SQL SERVER误解。比如:
select * from table1 where name=''zhangsan'' and tID > 10000 和执?
select * from table1 where tID > 10000 and name=''zhangsan'' 一些h不知道以上两条语句的执行效率是否一
P因ؓ如果单的从语句先后上看,q两个语句的是不一P如果tID是一个聚合烦引,那么后一句仅仅从表的
10000条以后的记录中查扑ְ行了Q而前一句则要先从全表中查找看有几个name=''zhangsan''的,而后再根据限制条?/p>
条gtID>10000来提出查询结果?/p>
事实上,q样的担心是不必要的。SQL SERVER中有一个“查询分析优化器”,它可以计出where子句中的搜烦条g
q确定哪个烦引能~小表扫描的搜烦I间Q也是_它能实现自动优化?/p>
虽然查询优化器可以根据where子句自动的进行查询优化,但大家仍然有必要了解一下“查询优化器”的工作原理Q?/p>
如非q样Q有时查询优化器׃不按照您的本意进行快速查询?/p>
在查询分析阶D,查询优化器查看查询的每个阶段q决定限刉要扫描的数据量是否有用。如果一个阶D可以被?/p>
作一个扫描参敎ͼSARGQ,那么q之ؓ可优化的Qƈ且可以利用烦引快速获得所需数据?/p>
SARG的定义:用于限制搜烦的一个操作,因ؓ它通常是指一个特定的匚wQ一个值得范围内的匚w或者两个以上条
件的ANDq接。Ş式如下:
列名 操作W?<常数 ?变量>
?
<常数 ?变量> 操作W列名 列名可以出现在操作符的一边,而常数或变量出现在操作符的另一辏V如Q?/p>
Name=’张三?
h>5000
5000<h
Name=’张三?and h>5000 如果一个表辑ּ不能满SARG的Ş式,那它无法限制搜索的范围了,也就是SQL
SERVER必须Ҏ一行都判断它是否满WHERE子句中的所有条件。所以一个烦引对于不满SARG形式的表辑ּ来说是无?/p>
的?/p>
介绍完SARG后,我们来ȝ一下用SARG以及在实践中遇到的和某些资料上结Z同的l验Q?/p>
1、Like语句是否属于SARG取决于所使用的通配W的cd
如:name like ‘张%?Q这属于SARG
而:name like ?张?,׃属于SARG。 原因是通配W?在字W串的开通得烦引无法用?/p>
2、or 会引起全表扫?/p>
Name=’张三?and h>5000 W号SARGQ而:Name=’张三?or h>5000 则不W合SARG。用or会引起全表扫
描?/p>
3、非操作W、函数引L不满SARG形式的语?/p>
不满SARG形式的语句最典型的情况就是包括非操作W的语句Q如QNOT?=?lt;>?<?>、NOT EXISTS、NOT IN?/p>
NOT LIKE{,另外q有函数。下面就是几个不满SARG形式的例子:
ABS(h)<5000
Name like ?三?
有些表达式,如:
WHERE h*2>5000
SQL SERVER也会认ؓ是SARGQSQL SERVER会将此式转化为:
WHERE h>2500/2 但我们不推荐q样使用Q因为有时SQL SERVER不能保证q种转化与原始表辑ּ是完全等L?/p>
4、IN 的作用相当与OR
语句Q?/p>
Select * from table1 where tid in (2,3)
?
Select * from table1 where tid=2 or tid=3 是一LQ都会引起全表扫描,如果tid上有索引Q其索引也会失效
?/p>
5、尽量少用NOT
6、exists ?in 的执行效率是一L
很多资料上都昄_exists要比in的执行效率要高,同时应尽可能的用not exists来代替not in。但事实上,?/p>
试验了一下,发现二者无论是前面带不带notQ二者之间的执行效率都是一L。因为涉及子查询Q我们试验这ơ用SQL
SERVER自带的pubs数据库。运行前我们可以把SQL SERVER的statistics I/O状态打开Q?/p>
Q?Qselect title,price from titles where title_id in (select title_id from sales where qty>30) 该句?/p>
执行l果为:
?''sales''。扫描计?18Q逻辑?56 ơ,物理?0 ơ,预读 0 ơ?br />?''titles''。扫描计?1Q逻辑?2 ơ,物理?0 ơ,预读 0 ơ?/p>
Q?Qselect title,price from titles
where exists (select * from sales
where sales.title_id=titles.title_id and qty>30) W二句的执行l果为:
?''sales''。扫描计?18Q逻辑?56 ơ,物理?0 ơ,预读 0 ơ?br />?''titles''。扫描计?1Q逻辑?2 ơ,物理?0 ơ,预读 0 ơ?/p>
我们从此可以看到用exists和用in的执行效率是一L?/p>
7、用函数charindex()和前面加通配W?的LIKE执行效率一?/p>
前面Q我们谈刎ͼ如果在LIKE前面加上通配W?Q那么将会引起全表扫描,所以其执行效率是低下的。但有的资料?/p>
l说Q用函数charindex()来代替LIKE速度会有大的提升Q经我试验,发现q种说明也是错误的:
select gid,title,fariqi,reader from tgongwen
where charindex(''刑侦支队'',reader)>0 and fariqi>''2004-5-5'' 用时Q?U,另外Q扫?/p>
计数 4Q逻辑?7155 ơ,物理?0 ơ,预读 0 ơ?/p>
select gid,title,fariqi,reader from tgongwen
where reader like ''%'' + ''刑侦支队'' + ''%'' and fariqi>''2004-5-5'' 用时Q?U,?/p>
外:扫描计数 4Q逻辑?7155 ơ,物理?0 ơ,预读 0 ơ?/p>
8、unionq不l对比or的执行效率高
我们前面已经谈到了在where子句中用or会引起全表扫描,一般的Q我所见过的资料都是推荐这里用union来代?/p>
or。事实证明,q种说法对于大部分都是适用的?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen
where fariqi=''2004-9-16'' or gid>9990000 用时Q?8U。扫描计?1Q逻辑?404008 ?/p>
Q物理读 283 ơ,预读 392163 ơ?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''
union
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where gid>9990000 用时Q?U。扫描计?8Q逻辑
?67489 ơ,物理?216 ơ,预读 7499 ơ?/p>
看来Q用union在通常情况下比用or的效率要高的多?/p>
但经q试验,W者发现如果or两边的查询列是一L话,那么用union则反倒和用or的执行速度差很多,虽然q里
union扫描的是索引Q而or扫描的是全表?br />
select gid,fariqi,neibuyonghu,reader,title from Tgongwen
where fariqi=''2004-9-16'' or fariqi=''2004-2-5'' 用时Q?423毫秒。扫描计?2Q逻辑
?14726 ơ,物理?1 ơ,预读 7176 ơ?/p>
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-9-16''
union
select gid,fariqi,neibuyonghu,reader,title from Tgongwen where fariqi=''2004-2-5'' 用时Q?1640毫秒。扫
描计?8Q逻辑?14806 ơ,物理?108 ơ,预读 1144 ơ?/p>
9、字D|取要按照“需多少、提多少”的原则Q避免“select *?/p>
我们来做一个试验:
select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc 用时Q?673毫秒
select top 10000 gid,fariqi,title from tgongwen order by gid desc 用时Q?376毫秒
select top 10000 gid,fariqi from tgongwen order by gid desc 用时Q?0毫秒
由此看来Q我们每提取一个字D,数据的提取速度׃有相应的提升。提升的速度q要看您舍弃的字D늚大小?/p>
判断?/p>
10、count(*)不比count(字段)?/p>
某些资料上说Q用*会统计所有列Q显然要比一个世界的列名效率低。这U说法其实是没有Ҏ的。我们来看:
select count(*) from Tgongwen 用时Q?500毫秒
select count(gid) from Tgongwen 用时Q?483毫秒
select count(fariqi) from Tgongwen 用时Q?140毫秒
select count(title) from Tgongwen 用时Q?2050毫秒
从以上可以看出,如果用count(*)和用count(主键)的速度是相当的Q而count(*)却比其他M除主键以外的字段?/p>
总速度要快Q而且字段长Q汇ȝ速度p慢。我惻I如果用count(*)Q?SQL SERVER可能会自动查找最字D|汇?/p>
的。当Ӟ如果您直接写count(主键)会来的更直接些?/p>
11、order by按聚集烦引列排序效率最?/p>
我们来看Q(gid是主键,fariqi是聚合烦引列Q:
select top 10000 gid,fariqi,reader,title from tgongwen 用时Q?96 毫秒?扫描计数 1Q逻辑?289 ơ,?/p>
理读 1 ơ,预读 1527 ơ?/p>
select top 10000 gid,fariqi,reader,title from tgongwen order by gid asc 用时Q?720毫秒?扫描计数 1Q?/p>
逻辑?41956 ơ,物理?0 ơ,预读 1287 ơ?/p>
select top 10000 gid,fariqi,reader,title from tgongwen order by gid desc 用时Q?736毫秒?扫描计数 1Q?/p>
逻辑?55350 ơ,物理?10 ơ,预读 775 ơ?/p>
select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi asc 用时Q?73毫秒?扫描计数 1
Q逻辑?290 ơ,物理?0 ơ,预读 0 ơ?/p>
select top 10000 gid,fariqi,reader,title from tgongwen order by fariqi desc 用时Q?56毫秒?扫描计数 1
Q逻辑?289 ơ,物理?0 ơ,预读 0 ơ?/p>
从以上我们可以看出,不排序的速度以及逻辑L数都是和“order by 聚集索引列?的速度是相当的Q但q些?/p>
比“order by 非聚集烦引列”的查询速度是快得多的?/p>
同时Q按照某个字D进行排序的时候,无论是正序还是倒序Q速度是基本相当的?/p>
12、高效的TOP
事实上,在查询和提取大定w的数据集Ӟ影响数据库响应时间的最大因素不是数据查找,而是物理的I/0操作?/p>
如:
select top 10 * from (
select top 10000 gid,fariqi,title from tgongwen
where neibuyonghu=''办公?'
order by gid desc) as a
order by gid asc q条语句Q从理论上讲Q整条语句的执行旉应该比子句的执行旉长,但事实相反。因为,?/p>
句执行后q回的是10000条记录,而整条语句仅q回10条语句,所以媄响数据库响应旉最大的因素是物理I/O操作。?/p>
限制物理I/O操作此处的最有效Ҏ之一是使用TOP关键词了。TOP关键词是SQL SERVER中经q系l优化过的一个用来提
取前几条或前几个癑ֈ比数据的词。经W者在实践中的应用Q发现TOP实很好用,效率也很高。但q个词在另外一个大
型数据库ORACLE中却没有Q这不能说不是一个遗憾,虽然在ORACLE中可以用其他ҎQ如QrownumberQ来解决。在以后
的关于“实现千万数据的分|C存储过E”的讨论中,我们将用到TOPq个关键词?/p>
到此为止Q我们上面讨Z如何实现从大定w的数据库中快速地查询出您所需要的数据Ҏ。当Ӟ我们介绍的这
些方法都是“Y”方法,在实践中Q我们还要考虑各种“硬”因素,如:|络性能、服务器的性能、操作系l的性能Q?/p>
甚至|卡、交换机{?br />实现数据量和v量数据的通用分页昄存储q程
建立一?Web 应用Q分|览功能必不可。这个问题是数据库处理中十分常见的问题。经典的数据分页Ҏ?/p>
:ADO U录集分|Q也是利用ADO自带的分功能(利用游标Q来实现分页。但q种分页Ҏ仅适用于较数据量?/p>
情ŞQ因为游标本w有~点Q游标是存放在内存中Q很费内存。游标一建立Q就相关的记录锁住Q直到取消游标。游
标提供了对特定集合中逐行扫描的手D,一般用游标来逐行遍历数据Q根据取出数据条件的不同q行不同的操作。?/p>
对于多表和大表中定义的游标(大的数据集合Q@环很Ҏ使程序进入一个O长的{待甚至L?/p>
更重要的是,对于非常大的数据模型而言Q分|索时Q如果按照传l的每次都加载整个数据源的方法是非常费
资源的。现在流行的分页Ҏ一般是索页面大的块区的数据,而非索所有的数据Q然后单步执行当前行?/p>
最早较好地实现q种Ҏ面大小和页码来提取数据的方法大概就是“俄|斯存储q程”。这个存储过E用了游?/p>
Q由于游标的局限性,所以这个方法ƈ没有得到大家的普遍认可?/p>
后来Q网上有人改造了此存储过E,下面的存储过E就是结合我们的办公自动化实例写的分存储过E:
CREATE procedure pagination1
(@pagesize int, --面大小Q如每页存储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 以上存储q程q用了SQL SERVER的最新技术――表变量。应该说q个存储q程也是一个非怼U?/p>
分页存储q程。当Ӟ在这个过E中Q您也可以把其中的表变量写成临时表:CREATE TABLE #Temp。但很明显,在SQL
SERVER中,用时表是没有用表变量快的。所以笔者刚开始用这个存储过E时Q感觉非常的不错Q速度也比原来的ADO
的好。但后来Q我又发C比此Ҏ更好的方法?br /> W者曾在网上看C一小短文《从数据表中取出Wn条到Wm条的记录的方法》,全文如下Q?/p>
从publish 表中取出W?n 条到W?m 条的记录Q?
SELECT TOP m-n+1 *
FROM publish
WHERE (id NOT IN
(SELECT TOP n-1 id
FROM publish))
id 为publish 表的关键? 我当时看到这文章的时候,真的是精ؓ之一振,觉得思\非常得好。等到后来,?/p>
在作办公自动化系l(ASP.NET+ C#QSQL SERVERQ的时候,忽然惌v了这文章,我想如果把这个语句改造一下,q就
可能是一个非常好的分存储过E。于是我满|上找这文章,没想刎ͼ文章q没扑ֈQ却扑ֈ了一根据此语句?/p>
的一个分存储过E,q个存储q程也是目前较ؓ行的一U分存储过E,我很后悔没有争先把这D|字改造成存储
q程Q?/p>
CREATE PROCEDURE pagination2
(
@SQL nVARCHAR(4000), --不带排序语句的SQL语句
@Page int, --늠
@RecsPerPage int, --每页容纳的记录数
@ID VARCHAR(255), --需要排序的不重复的ID?
@Sort VARCHAR(255) --排序字段及规?
)
AS
DECLARE @Str nVARCHAR(4000)
SET @Str=''SELECT TOP ''+CAST(@RecsPerPage AS VARCHAR(20))+'' * FROM
(''+@SQL+'') T WHERE T.''+@ID+''NOT IN (SELECT TOP ''+CAST((@RecsPerPage*(@Page-1))
AS VARCHAR(20))+'' ''+@ID+'' FROM (''+@SQL+'') T9 ORDER BY ''+@Sort+'') ORDER BY ''+@Sort
PRINT @Str
EXEC sp_ExecuteSql @Str
GO 其实Q以上语句可以简化ؓQ?/p>
SELECT TOP 大?*
FROM Table1 WHERE (ID NOT IN (SELECT TOP 大?| id FROM ?ORDER BY id))
ORDER BY ID 但这个存储过E有一个致命的~点Q就是它含有NOT IN字样。虽然我可以把它攚wؓQ?/p>
SELECT TOP 大?*
FROM Table1 WHERE not exists
(select * from (select top (大?|) * from table1 order by id) b where b.id=a.id )
order by id 卻I用not exists来代替not inQ但我们前面已经谈过了,二者的执行效率实际上是没有区别的。既?/p>
如此Q用TOP l合NOT IN的这个方法还是比用游标要来得快一些?/p>
虽然用not existsq不能挽救上个存储过E的效率Q但使用SQL SERVER中的TOP关键字却是一个非常明智的选择。因
为分优化的最l目的就是避免生过大的记录集,而我们在前面也已l提CTOP的优势,通过TOP 卛_实现Ҏ据量
的控制?/p>
在分늮法中Q媄响我们查询速度的关键因素有两点QTOP和NOT IN。TOP可以提高我们的查询速度Q而NOT IN会减
慢我们的查询速度Q所以要提高我们整个分页法的速度Q就要彻底改造NOT INQ同其他Ҏ来替代它?/p>
我们知道Q几乎Q何字D,我们都可以通过max(字段)或min(字段)来提取某个字D中的最大或最|所以如果这
个字D不重复Q那么就可以利用q些不重复的字段的max或min作ؓ分水岭,使其成ؓ分页法中分开每页的参照物。在
q里Q我们可以用操作W?gt;”或?lt;”号来完成这个命,使查询语句符合SARG形式。如Q?/p>
Select top 10 * from table1 where id>200 于是有了如下分|案:
select top 大?*
from table1
where id>
(select max (id) from
(select top ((늠-1)*大? id from table1 order by id) as T
)
order by id 在选择即不重复|又容易分辨大的列时Q我们通常会选择主键。下表列ZW者用有着1000万数?/p>
的办公自动化pȝ中的表,在以GIDQGID是主键,但ƈ不是聚集索引。)为排序列、提取gid,fariqi,title字段Q分?/p>
以第1?0?00?00?000?万?0万?5万?0万页ZQ测试以上三U分|案的执行速度Q(单位Q毫U)
늠 Ҏ1 Ҏ2 Ҏ3
1 60 30 76
10 46 16 63
100 1076 720 130
500 540 12943 83
1000 17110 470 250
10000 24796 4500 140
100000 38326 42283 1553
250000 28140 128720 2330
500000 121686 127846 7168
从上表中Q我们可以看出,三种存储q程在执?00以下的分页命oӞ都是可以信Q的,速度都很好。但W一U?/p>
Ҏ在执行分?000以上后Q速度降了下来。第二种Ҏ大约是在执行分页1万页以上后速度开始降了下来。而第?/p>
U方案却始终没有大的降势Q后劲仍然很?br /> 在确定了W三U分|案后Q我们可以据此写一个存储过E。大家知道SQL SERVER的存储过E是事先~译好的SQL?/p>
句,它的执行效率要比通过WEB面传来的SQL语句的执行效率要高。下面的存储q程不仅含有分页ҎQ还会根据页?/p>
传来的参数来定是否q行数据Ll计?/p>
--获取指定늚数据Q?/p>
CREATE PROCEDURE pagination3
@tblName varchar(255), -- 表名
@strGetFields varchar(1000) = ''*'', -- 需要返回的列?
@fldName varchar(255)='''', -- 排序的字D名
@PageSize int = 10, -- 尺?
@PageIndex int = 1, -- 늠
@doCount bit = 0, -- q回记录L, ?0 值则q回
@OrderType bit = 0, -- 讄排序cd, ?0 值则降序
@strWhere varchar(1500) = '''' -- 查询条g (注意: 不要?where)
AS
declare @strSQL varchar(5000) -- 主语?
declare @strTmp varchar(110) -- 临时变量
declare @strOrder varchar(400) -- 排序cd
if @doCount != 0
begin
if @strWhere !=''''
set @strSQL = "select count(*) as Total from [" + @tblName + "] where "+@strWhere
else
set @strSQL = "select count(*) as Total from [" + @tblName + "]"
end --以上代码的意思是如果@doCount传递过来的不是0Q就执行Ll计。以下的所有代码都是@doCount?的情况:
else
begin
if @OrderType != 0
begin
set @strTmp = "<(select min"
set @strOrder = " order by [" + @fldName +"] desc"--如果@OrderType不是0Q就执行降序Q这句很重要Q?/p>
end
else
begin
set @strTmp = ">(select max"
set @strOrder = " order by [" + @fldName +"] asc"
end
if @PageIndex = 1
begin
if @strWhere != ''''
set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "
from [" + @tblName + "] where " + @strWhere + " " + @strOrder
else
set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ "
from ["+ @tblName + "] "+ @strOrder--如果是第一就执行以上代码Q这样会加快执行速度
end
else
begin--以下代码赋予了@strSQL以真正执行的SQL代码
set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["
+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["+ @fldName + "])
from (select top " + str((@PageIndex-1)*@PageSize) + " ["+ @fldName + "]
from [" + @tblName + "]" + @strOrder + ") as tblTmp)"+ @strOrder
if @strWhere != ''''
set @strSQL = "select top " + str(@PageSize) +" "+@strGetFields+ " from ["
+ @tblName + "] where [" + @fldName + "]" + @strTmp + "(["
+ @fldName + "]) from (select top " + str((@PageIndex-1)*@PageSize) + " ["
+ @fldName + "] from [" + @tblName + "] where " + @strWhere + " "
+ @strOrder + ") as tblTmp) and " + @strWhere + " " + @strOrder
end
end
exec (@strSQL)
GO
上面的这个存储过E是一个通用的存储过E,其注释已写在其中了?在大数据量的情况下,特别是在查询最后几?/p>
的时候,查询旉一般不会超q?U;而用其他存储q程Q在实践中就会导致超Ӟ所以这个存储过E非帔R用于大定w
数据库的查询?W者希望能够通过对以上存储过E的解析Q能l大家带来一定的启示Qƈl工作带来一定的效率提升Q?/p>
同时希望同行提出更优U的实时数据分늮法?/p>
聚集索引的重要性和如何选择聚集索引
x我们讨论了实现小数据量和量数据的通用分页昄存储q程。这是因为在本存储q程应用于“办公自动化
”系l的实践中时Q笔者发现这W三U存储过E在数据量的情况下Q有如下现象Q?/p>
1、分速度一般维持在1U和3U之间?/p>
2、在查询最后一|Q速度一般ؓ5U至8U,哪怕分|L只有3|30万页?/p>
虽然在超大容量情况下Q这个分늚实现q程是很快的Q但在分前几|Q这?Q?U的速度比vW一U甚x?/p>
l过优化的分|法速度q要慢,借用L话说是“还没有ACCESS数据库速度快”,q个认识以D用户攑ּ使用
您开发的pȝ?/p>
W者就此分析了一下,原来产生q种现象的症l是如此的简单,但又如此的重要:排序的字D不是聚集烦引!
本篇文章的题目是Q“查询优化及分页法Ҏ”。笔者只所以把“查询优化”和“分늮法”这两个联系不是?/p>
大的论题攑֜一P是因ؓ二者都需要一个非帔R要的东西――聚集烦引?/p>
在前面的讨论中我们已l提CQ聚集烦引有两个最大的优势Q?/p>
1、以最快的速度~小查询范围?/p>
2、以最快的速度q行字段排序?/p>
W?条多用在查询优化Ӟ而第2条多用在q行分页时的数据排序?/p>
而聚集烦引在每个表内又只能徏立一个,q得聚集烦引显得更加的重要。聚集烦引的挑选可以说是实现“查询优
化”和“高效分”的最关键因素?br /> 但要既聚集索引列既W合查询列的需要,又符合排序列的需要,q通常是一个矛盾。笔者前面“烦引”的讨论?/p>
Q将fariqiQ即用户发文日期作ؓ了聚集烦引的起始列,日期的精度为“日”。这U作法的优点Q前面已l提CQ?/p>
在进行划旉D늚快速查询中Q比用ID主键列有很大的优ѝ?br /> 但在分页Ӟ׃q个聚集索引列存在着重复记录Q所以无法用max或min来最为分늚参照物,q而无法实现更
为高效的排序。而如果将ID主键列作集烦引,那么聚集索引除了用以排序之外Q没有Q何用处,实际上是费了聚
集烦引这个宝늚资源?br /> 册个矛盾,W者后来又d了一个日期列Q其默认gؓgetdate()。用户在写入记录Ӟq个列自动写入当?/p>
的时_旉_到毫U。即使这PZ避免可能性很的重合Q还要在此列上创建UNIQUEU束。将此日期列作ؓ?/p>
集烦引列?br /> 有了q个旉型聚集烦引列之后Q用户就既可以用q个列查扄户在插入数据时的某个旉D늚查询Q又可以作ؓ
唯一列来实现max或minQ成为分늮法的参照物?br /> l过q样的优化,W者发玎ͼ无论是大数据量的情况下还是小数据量的情况下,分页速度一般都是几十毫U,甚至0
毫秒。而用日期D늾范围的查询速度比原来也没有Mq钝。聚集烦引是如此的重要和珍贵Q所以笔者ȝ了一下,
一定要聚集烦引徏立在Q?/p>
1、您最频繁使用的、用以羃查询范围的字段上;
2、您最频繁使用的、需要排序的字段上?/p>
l束?/p>
本篇文章汇集了笔者近D在使用数据库方面的心得Q是在做“办公自动化”系l时实践l验的积累。希望这文?/p>
不仅能够l大家的工作带来一定的帮助Q也希望能让大家能够体会到分析问题的ҎQ最重要的是Q希望这文章能?/p>
抛砖引玉Q掀起大家的学习和讨论的兴趣Q以共同促进Q共同ؓ公安U技事业和金监ַE做己最大的努力?/p>
最后需要说明的是,在试验中Q我发现用户在进行大数据量查询的时候,Ҏ据库速度影响最大的不是内存大小Q?/p>
而是CPU。在我的P4 2.4机器上试验的时候,查看“资源管理器”,CPUl常出现持箋?00%的现象,而内存用量却q没
有改变或者说没有大的改变。即使在我们的HP ML 350 G3服务器上试验ӞCPU峰g能达?0%Q一般持l在70%左右?/p>
本文的试验数据都是来自我们的HP ML 350服务器。服务器配置Q双Inter Xeon 线E?CPU 2.4GQ内?GQ操作系
lWindows Server 2003 Enterprise EditionQ数据库SQL Server 2000 SP3
如果希望COUNT函数对给定列的所有行Q包括空|q行计数Q请使用ISNULL函数。ISNULL函数会将I值替换成有效的倹{?br />
事实上,寚w合函数来_如果I值可能导致错误结果,ISNULL函数非常有用。记住在使用一个星hQCOUNT函数会对所有行q行计算。下例演CZI值在AVG和COUNT集合函数中的影响Q?
SET NOCOUNT ON
GO
CREATE TABLE xCount
(pkey1 INT IDENTITY NOT NULL
CONSTRAINT pk_xCount PRIMARY KEY,
Col1 int NULL)
GO
INSERT xCount (Col1) VALUES (10)
GO
INSERT xCount (Col1) VALUES (15)
GO
INSERT xCount (Col1) VALUES (20)
GO
INSERT xCount (Col1) VALUES (NULL)
GO
SELECT AVG(Col1) AvgWithoutIsNullFunctionOnCol1,
AVG(ISNULL(Col1,0)) AvgWithIsNullFunctionOnCol1,
COUNT(Col1) NoIsNullFunctionOnCol1 ,
COUNT(ISNULL(Col1,0)) UsingIsNullFunctionOnCol1,
Count(*) UsingAsterisk
FROM xCount
GO
DROP TABLE xCount
GO
OUTPUT:
AvgWOIsNullFnctnCol1 AvgWIsNullFnctnCol1 WOIsNullFnctnCol1
WIsNullFnctnCol1 UsingAsterisk
---------------- ------------- -------------- ------------ ---------
15 11 3 4 4
恰当使用I?/pre>SQL Server可能出现一U特D情况:在引用父表的一个表中,因ؓ不允许空|所以“声明引用完整性”(DRIQ可能不会得到强制。即使父表不包含I|在子表引用了父表主键U束或惟一U束的列中,也可能包含空倹{?br />
假如来自父表的值目前未知,׃会有M问题。例如,父表可能是一个地址表,而子表可能包含联pM息。由于许多原因,可能暂时不知道要传给父表的联pd址。这是一U基于时间的问题Q空值在其中或许是合适的?/p>
如下例所C,我们创徏父表Qƈ在其中插入两个倹{?/p>
SET NOCOUNT ON
GO
CREATE TABLE Parent
(pkey1 INT IDENTITY NOT NULL
CONSTRAINT pkParent PRIMARY KEY,
col1 INT NULL)
GO
INSERT Parent (col1) VALUES (284)
GO
INSERT Parent (col1) VALUES (326)
GO
以下代码则创建子表,q在引用父表的列中插入一个空倹{?CREATE TABLE Child
(pkey1 INT IDENTITY
CONSTRAINT pkChild PRIMARY KEY,
Parentpkey1 INT NULL
CONSTRAINT fkChildParent FOREIGN KEY
REFERENCES Parent(pkey1),
col1 INT NULL)
GO
INSERT Child (Parentpkey1, col1) VALUES (null,2)
GO
但在以下代码中,要同时从父表和子表选择倹{虽然父表不包含I|但在子表引用了父表的那个列中Q将允许一个空倹{?br />然后丢弃所有表Q清除这个演C所用的数据库对象?br />
SELECT * FROM Child
GO
SELECT * FROM Parent
GO
DROP TABLE Child, Parent
GO
A:多表查询和笛儿U?br />到目前ؓ?我们所用的都是单个表查?但是在更多的情况的下,需要对多个表进行同时查?q时可以把多个表的名字全部填写在from子句?
比如:查询出每个职工的姓名,学历,所在部门名U?׃我们需要的l果来自于两个表,所以必ȝ多表查询
select 姓名,学历,部门名称,负责?from work,部门 [分析Z么是错误的]
原因:问题出在对表D接条件的限制?在上面的语句?没能对表D接条件作M限制,所以sql会在work表中每取Z条记?׃部门表中的所有记录组合一?那么假设work表有m条记?而部门表中有n条记?则得出的l果为m*n条记录这是W尔儿积,所以笛儿U返回的大多数的l果是冗余的、无用的,所以应该避免笛儿U的产生.
解决W尔儿积的方?事实上由于笛儿U是因ؓ两个表的q接条g没有限制造成?所以只要我们对两个表的q接q行条g限制,可以避免笛儿U的产生.可以通过一个where子句,来连接两个表的公q字段可以了.
所以将上面的语句改?select 姓名,学历,部门名称,负责?from work,部门 where work.部门~号=部门.部门~号
B:使用表格的别?br /> A:当用多个表q行查询?如果有两个表中有相同的列,应该指明选中的是哪个表中的列.
比如:在work表检索出在address表中都有的职工的职工?姓名,学历,基本工资
select 职工?姓名,学历,基本工资 from work,address where work.职工?address.职工?br /> 上面的语句是错误?原因是对于work和address表都有职工号,姓名?所以应该指明是哪个表的职工号和姓名.
Ҏ:select work.职工?work.姓名,学历,基本工资 from work,address where work.职工?address.职工?br /> 或?select address.职工?address.姓名,学历,基本工资 from work,address where work.职工?address.职工?br /> 想一?Z么对于学?基本工资没有指明表名:?work.学历,work.基本工资[只有一个表有这些列]
B:允许使用别名来访问表.
格式:1:表名 as 别名
2:表名 别名
例如:上面的语句可改写?
select w.职工?w.姓名,学历,基本工资 from work as w,address as a where w.职工?a.职工?
上面的语句中在from中引用两个表,q且work指明了别名w,address指明了别名a,所以就可以用w来代表work?用a来代表address?或者省略as直接Ҏ:select w.职工?w.姓名,学历,基本工资 from work w,address a where w.职工?a.职工?
C:如果使用了别?则以后所有查询语句中,都必M用别名列
比如:select work.职工?work.姓名,学历,基本工资 from work w,address a where w.职工?a.职工?[是错误的]
C:使用l计函数:
sql跟我们提供了以下几个l计函数:
sum:q回一个数字列的d
avg:对一个数字列求^均?br /> min:对一个数字列求最?br /> max:对一个数字列求最大?br /> count:q回满select语句中指定的条g的记录个?br /> 丑ֈ:1:求出work表中所有男职工的基本工资的?br /> select sum(基本工资) as 性别为男的基本工?from work where 性别=\'男\'
2:求出work表中所有职U是l理的最高工资和最低工?q_工资
select max(基本工资) as 最高工?min(基本工资) as 最低工?avg(q_工资) as q_工资 from work
3:与统计函C起用distinct关键字[通常只与count函数使用]
?1:索出work表中学历的个?br /> select count(学历) from work
2:索出work表中学历的种cȝ个数
select count(distinct 学历) from work
试一?select distinct count(学历) from work 可行?
3:有work和部门表,索出在销售部工作的员工的个数
select \'销售部的h数\'=count(职工? from work a,部门 b
where a.部门~号=b.部门~号 and b.部门名称=\'销售部\'
4:在work表中索出其基本工资小于职工^均工资的人数
select count(职工? as 人数 from work
where 基本工资<(select avg(基本工资) from work)
5:有学U表和学费表,从学费表索出有多个学网设计的?br /> select count(学号) as |页设计的h?from 学费 a,学科 b
where a.所学专业代?b.评~号 and b.评名称=\'|页设计\'
D:使用group by子句对结果进行分c[只用于统计函数]
丑ֈ:1:索出work表各职称的h?
select 职称,count(职称) as 职称人数 from work group by 职称
2:索出各学历的q_工资.
select 学历,avg(基本工资) from work group by 学历
3:有学U表和学费表,要求l计出各学科的学生数?
select 所学专业代?count(所学专业代? as 人数 into #abc from 学费 group by 所学专业代?br /> select 评名称,人数 from #abc,学科 where 所学专业代?评~号
4:有职工表和商品销售表,要求索出每个职工的职工号,姓名,销售总量.
select 职工?sum(销售量) as 销售总量 into #abcd from 商品销?group by 职工?br /> select 职工.职工?姓名,销售总量 from 职工,#abcd where #abcd.职工?职工.职工?br /> 5:查询出每个部门最高的基本工资,昄部门名称和最高基本工?br /> select 部门名称,max(基本工资) from work group by 部门名称
说明:1:在group by中不支持对列名的分配的别?br /> select 学历 as 职工学历,count(学历) from work group by 职工学历 [错错]
改ؓ:select 学历 as 职工学历,count(学历) from work group by 学历
2:select后面每一列数据除了在l计函数中的列以外都必须在group by子句出现
比如:select 学历,性别,sum(基本工资) from work group by 学历[错错]
改ؓ:select 学历,性别,sum(基本工资) from work group by 学历,性别
意义:各学历各性别的基本工资之?br />
transact---sql高查询Q下Q?br />5:使用having关键字来{选结?br />6:使用compute和compute by子句
7:使用嵌套查询
8:分布式查?/p>
E:使用having关键字来{选结?br /> 当完成对数据l果的查询和l计?可以使用having关键字来Ҏ询和计算的结果进行一步的{?br /> ?索出work表中学历是大专或者是中专的h?br /> select 学历,count(学历) from work group by 学历 having 学历 in(\'大专\',\'中专\')
说明:1:having关键字都与group by用在一?
2:having不支持对列分配的别名
例如:select 学历,\'大于5的h数\'=count(学历) from work group by 学历 having 大于5的h?gt;5 [错错]
改ؓ:select 学历,\'大于5的h数\'=count(学历) from work group by 学历 having count(学历)>5
F:使用compute和compute by
使用compute子句允许同时观察查询所得到各列的数据的l节以及l计各列数据所产生的汇d
select * from work [查询所得到的各列的数据的细节]
compute max(基本工资),min(基本工资) [l计之后的结果]
q个例子中没有用by关键?q回的结果是最后添加了一行基本工资的最大值和最?也可增加by关键?
?select * from work order by 学历
compute max(基本工资),min(基本工资) by 学历
比较:select 学历,max(基本工资),min(基本工资) from work group by 学历
说明:1:compute子句必须与order by子句用在一?br /> 2:compute子句可以q回多种l果?一U是体现数据l节的数据集,可以按分c要求进行正的分类Q另一U在分类的基上进行汇M生结?
3:而group by子句Ҏ一cL据分cM后只能生一个结?不能知道l节
G:使用嵌套查询
查询中再查询,通常是以一个查询作为条件来供另一个查询?br /> ?有work表和部门?br /> A:索出在部门表中登记的所有部门的职工基本资料
select * from work where 部门~号 in [not in](select 部门~号 from dbo.部门)
B:索出在work表中每一个部门的最高基本工资的职工资料
select * from work a where 基本工资=(select max(基本工资) from work b where a.部门名称=b.部门名称)
说明:由外查询提供一个部门名U给内查?内查询利用这个部门名U找到该部门的最高基本工?然后外查询根据基本工资判断是否等于最高工?如果是的,则显C出?
相当?select * from work,(select 部门名称,max(基本工资) as 基本工资 from work group by 部门名称 as t) where work.基本工资=t.基本工资 and work.部门名称=t.部门名称
C:用嵌套work表和嵌套部门?在嵌套work表中索出姓名和职工号都在嵌套部门存在的职工资?
select * from 嵌套work where 职工?in (select 职工?from 嵌套部门) and 姓名 in (select 姓名 from 嵌套部门) [察看l果,分析原因]
?select * from 嵌套work a,嵌套部门 b where a.职工?b.职工?and a.姓名=b.姓名
?select * from 嵌套work where 职工?(select 职工?from 嵌套部门) and 姓名=(select 姓名 from 嵌套部门) [行吗?Z?分析原因?]
在嵌套中使用exists关键字[存在]
?1:用嵌套work表和嵌套部门?在嵌套work表中索出姓名和职工号都在嵌套部门存在的职工资?
select * from 嵌套work a where exists (select * from 嵌套部门 b where a.姓名=b.姓名 and a.职工?b.职工?
2:在work表检索出在部门表没有的职?br /> select * from work where not exists (select * from 部门 where 部门.部门~号=work.部门~号)
能否Ҏ:select * from work where exists (select * from 部门 where 部门.部门~号<>work.部门~号)
在列清单中用select
?1:在work1表和部门表中索出所有部门的部门名称和基本工资d
select 部门名称,(select sum(基本工资) from work1 b where a.部门~号=b.部门~号) from 部门 a
2:索各部门的职工h?br /> select 部门~号,部门名称,(select count(职工? from work1 a where a.部门~号=b.部门~号) as 人数 from 部门 b
3:在商品表和销售表中查询每一职工的姓?所属部?销售总量
select 姓名,所属部?(select sum(销售量) from 商品销?a where a.职工?b.职工? as 销售总量 from 嵌套部门 b
H:分布式查?br />我们以前的查询都只是Z一个服务器中的一个数据库的查?如果一个查询是要跨一个服务器,像这L查询是分布式查?那么我们以看到分布查询就是数据源自于两个服务?要进行分布式查询必须先创Z个“链接服务器?以便让本地的用户能够映射到过E服务器.
“链接服务器”的创立
A:在“链接服务器”里面输入以后ؓ了方便访问该链接服务器的名称[L]
B:在“提供程序名U”里面选择“Microsoft OLE DB Provider for SQL Server?br /> C:在“数据源”里面输入服务器的网l名
D:本地d,q程用户和远E密码里面分别输入一个本地登录用?q程d和远E密码以便让本地SQL Serverd映射为链接服务器上的用户
E:讉KҎ:格式:链接服务器的名称.数据库名.dbo.表名
链接服务器有两个特点:
1:通过链接服务器不能删除链接源服务器的M对像.
2:能过链接服务器可以对链接源服务器的表q行insert,updae,delete操作.
视图
1:什么是视图
2:视图和查询的区别
3:视图的优?br />4:如何创徏和管理视?br />5:如何通过视图修改基本表的数据
6:如何通过视图实现数据的安全?/p>
A:什么是视图:
视图(view):从一个或几个基本表中Ҏ用户需要而做成一个虚?br /> 1:视图是虚?它在存储时只存储视图的定?而没有存储对应的数据
2:视图只在刚刚打开的一瞬间,通过定义从基表中搜集数据,q展现给用户
B:视图与查询的区别:
视图和查询都是用由sql语句l成,q是他们相同的地?但是视图和查询有着本质区别:
它们的区别在?1:存储上的区别:视图存储为数据库设计的一部分,而查询则不是.
2:更新限制的要求不一?br /> 要注?因ؓ视图来自于表,所以通过视图可以间接对表q行更新,我们也可以通过update语句对表q行更新,但是对视囑֒查询更新限制是不同的,以下我们会知道虽焉过视图可以间接更新表但是有很多限制.
3:排序l果:通过sql语句,可以对一个表q行排序,而视囑ֈ不行.
比如:创徏一个含有order by子句的视?看一下可以成功吗?
C:视图的优?
Z么有了表q要引入视图呢?q是因ؓ视图h以下几个优点:
1:能分割数?化观?br /> 可以通过select和where来定义视?从而可以分割数据基表中某些对于用户不关心的数据,使用h注意力集中到所兛_的数据列.q一步简化浏览数据工?
2:为数据提供一定的逻辑独立?br /> 如果为某一个基表定义一个视?即以后基本表的内容的发生改变了也不会媄响“视囑֮义”所得到的数?br />3:提供自动的安全保护功?br /> 视图能像基本表一h予或撤消讉K许可?
4:视图可以间接对表q行更新,因此视图的更新就是表的更?/p>
D:视图的创建和理
视图的创?br /> 1:通过sql语句
格式:create view 视图?as select 语句
试一?分别创徏关于一个表或多个表的视图[因ؓ视图可以来自于多表]
2:通过企业理器 ?
说明:1:在完成视囄创立之后,可以像使用基本表一h使用视图
2:在创图时,q所有的select子查询都可用
?compute和compute by,order by[除非与top一赯用]
3:但在查询?依然都可以用在创建时用的select子查?br /> 4:在视囑ֈ建时,必须为没有标题列指定标题[思?能否不用select语句来创Z个视图]
视图的删?
1:通过sql语句:drop view 视图?br /> 2:通过企业理?br /> 说明:与删除表不同的是,删除视图后只是删除了视图了定?q没有删除表中的数据.[查看相关性]
修改视图的定?br /> 1:通过企业理?br /> 2:通过sql语句:
格式:alter view 视图?as 新的select语句
览视图信息 sp_helptext 视图?[查看视图创徏的语句]
E:如何通过视图修改基本表的数据.
1:在视图上使用insert语句
通过视图插入数据与直接在表中插入数据一?但视图毕竟不是基本表.因此在进行数据插入时q是有一定的限制
1:如果视图上没有包括基本表中属性ؓnot null[不能为空]的列,那么插入操作会因为那些列是nullD失?
2:如果某些列因为某些规则或U束的限制而不能直接接受从视图插入的列?插入会失?br /> 3:如果在视图中包含了用统计函数的l果,或是包含计算?则插入操作会p|
4:不能在用了distinct语句的视图中插入?br /> 5:不能在用了group by语句的视图中插入?/p>
2:使用update更新视图中的数据
1:更新视图与更新表g?但是在视图中使用了多个基本表q接的情况下,每次更新操作只能更新来自基本表的一个数据列
例如:创徏以下视图:create view del as
select 职工?姓名,部门名称,负责?from work1,部门
where work1.部门~号=部门.部门~号
如果再执行下面的语句?
update del set 职工?\'001\',部门名称=\'wenda\' where 职工?\'01\'[出现错误]
只能够改?update del set 职工?\'001\' where 职工?\'01\'
update del set 部门名称=\'wenda\' where 职工?\'01\'
2:不能在用了distinct语句的视图中更新?br /> 3:不能在用了group by语句的视图中更新?br />
3:使用delete删除视图中数?
通过视图删除数据最l体Cؓ从基本表中删除数?br /> 格式:delete 视图?[where 条g]
说明:当视囄两个以上的基表构成时,不允许删除视囄数据
例如:Z个视图kk
create view kk as
select 职工?姓名,性别,部门名称 from work1,部门 where work1.部门~号=部门.部门~号 [试着d除]
使用with check option的视?br /> 如果不了解视囑֮义内?则常怼发生向视图中输入不符合视囑֮义的数据的情?
比如:create view xm as
select * from work where 性别=\'男\'
完全可以插入insert xm values(\'001\',\'女\',23,\'2400\'....)
管从意义上来说是不合理?但是上述语句是正的.Z防止q种情况的发?可以使用with check option子句来对插入的或更改的数据进行限?
比如:create view xm as
select * from work where 性别=\'男\' with check option
使用schemabinding的视图[使用l定到构架]
我们知道视图是依赖于?如果在一个表中创Z个视?今后如果q个表被删除?则这个视囑ְ不可再用?Z防止用户删除一个有视图在引用的?可以在创囄时候加上schemabinding关键?
比如:create view 基本工资 with SCHEMABINDING
as select 姓名,性别,基本工资 from dbo.work
说明:1:不能使用?”来创徏此类型的视图
2:创徏此类型的视图?一定要加上dbo.表名.
3:如果在某个表中定义了此类视图,则用户将不能对表的结构进行修?否则会删除这些绑?br /> 4:如果用户对表的结构进行列改名,则会删除l定而且视图不可?
5:如果用户对表的结构进行列的类型或者大修?则会删除l定但视囑֏?此时用户可以删除视图所引用的表.
使用with encryption对视图进行加?br />Z保护创徏视图定义的原代码,可以对视图进行加?
比如:create view kk with encryption
as select * from work where 职称=\'l理\'
用sp_helptext来查看一?或用企业理器查看一?
说明:如果应用此项用户无法设计视?/p>
F:使用视图加强数据的安?br /> 一般通过使用视图共有三种途径加强数据的安全性 ?
A:对不同用h予不同的使用?
B:通过使用select子句限制用户Ҏ些底层基表的列的讉K
C:通过使用where子句限制用户Ҏ些底层基表的行的讉K
对不同用h予不同的权限
q接查询
通过q接q算W可以实现多个表查询。连接是关系数据库模型的主要特点Q也是它区别于其它类型数据库理pȝ的一个标志?
在关pL据库理pȝ中,表徏立时各数据之间的关系不必定Q常把一个实体的所有信息存攑֜一个表中。当索数据时Q通过q接操作查询出存攑֜多个表中的不同实体的信息。连接操作给用户带来很大的灵zL,他们可以在Q何时候增加新的数据类型。ؓ不同实体创徏新的表,后通过q接q行查询?
q接可以在SELECT 语句的FROM子句或WHERE子句中徏立,似是而非在FROM子句中指接时有助于将q接操作与WHERE子句中的搜烦条g区分开来。所以,在Transact-SQL中推荐用这U方法?
SQL-92标准所定义的FROM子句的连接语法格式ؓQ?
FROM join_table join_type join_table
[ON (join_condition)]
其中join_table指出参与q接操作的表名,q接可以对同一个表操作Q也可以对多表操作,对同一个表操作的连接又U做自连接?
join_type 指出q接cdQ可分ؓ三种Q内q接、外q接和交叉连接。内q接(INNER JOIN)使用比较q算W进行表间某(?列数据的比较操作Qƈ列出q些表中与连接条件相匚w的数据行。根据所使用的比较方式不同,内连接又分ؓ{D接、自然连接和不等q接三种?
外连接分为左外连?LEFT OUTER JOIN或LEFT JOIN)、右外连?RIGHT OUTER JOIN或RIGHT JOIN)和全外连?FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是,外连接不只列Zq接条g相匹配的行,而是列出左表(左外q接?、右?叛_q接?或两个表(全外q接?中所有符合搜索条件的数据行?
交叉q接(CROSS JOIN)没有WHERE 子句Q它q回q接表中所有数据行的笛卡尔U,其结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以W二个表中符合查询条件的数据行数?
q接操作中的ON (join_condition) 子句指出q接条gQ它pq接表中的列和比较运符、逻辑q算W等构成?
无论哪种q接都不能对text、ntext和image数据cd列进行直接连接,但可以对q三U列q行间接q接。例如:
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)
(一)内连?
内连接查询操作列Zq接条g匚w的数据行Q它使用比较q算W比较被q接列的列倹{内q接分三U:
1、等D接:在连接条件中使用{于?=)q算W比较被q接列的列|其查询结果中列出被连接表中的所有列Q包括其中的重复列?
2、不{连接: 在连接条件用除{于q算W以外的其它比较q算W比较被q接的列的列倹{这些运符包括>?gt;=?lt;=?lt;?>?<?lt;>?
3、自然连接:在连接条件中使用{于(=)q算W比较被q接列的列|但它使用选择列表指出查询l果集合中所包括的列Qƈ删除q接表中的重复列?
例,下面使用{D接列出authors和publishers表中位于同一城市的作者和出版C:
SELECT *
FROM authors AS a INNER JOIN publishers AS p
ON a.city=p.city
又如使用自然q接Q在选择列表中删除authors 和publishers 表中重复?city和state)Q?
SELECT a.*,p.pub_id,p.pub_name,p.country
FROM authors AS a INNER JOIN publishers AS p
ON a.city=p.city
(?外连?
内连接时Q返回查询结果集合中的仅是符合查询条? WHERE 搜烦条g?HAVING 条g)和连接条件的行。而采用外q接Ӟ它返回到查询l果集合中的不仅包含W合q接条g的行Q而且q包括左?左外q接?、右?叛_q接?或两个边接表(全外q接)中的所有数据行?
如下面用左外连接将论坛内容和作者信息连接v来:
SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b
ON a.username=b.username
下面使用全外q接city表中的所有作者以及user表中的所有作者,以及他们所在的城市Q?
SELECT a.*,b.*
FROM city as a FULL OUTER JOIN user as b
ON a.username=b.username
(?交叉q接
交叉q接不带WHERE 子句Q它q回被连接的两个表所有数据行的笛卡尔U,q回到结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以W二个表中符合查询条件的数据行数?
例,titles表中?cd书,而publishers表中?家出版社Q则下列交叉q接索到的记录数等
?*8=48行?
SELECT type,pub_name
FROM titles CROSS JOIN publishers
ORDER BY typ