sql温习笔记
-- 查询所有数据库
use master
select * from sysdatabases where dbid > 4 ; -- 系统自带的数据库分别是master->1,model->3,msdb->4,tempdb->2
-- 查询数据库中所有数据库(存储过程)
exec sp_helpdb;
-- 查询指定数据库中的表
use master
select * from sysobjects where xtype = ' u ' ;
if object_id ( ' #test1 ' ) is not null
drop table #test1
go
create table #test1
(
id int not null primary key ,
name nvarchar default ( ' haha ' )
)
drop table #test1
set nocount off
select name from sysobjects where xtype = ' u ' -- 读数据库中表名
select name from syscolumns where id = ( select max (id) from sysobjects where xtype = ' u ' and name = ' 表名 ' ) -- 读取某表的列名
-- exec调用保存在变量中的批处理代码
declare @sql as varchar ( 100 );
set @sql = ' print '' this is a message......; '' ; ' ;
exec ( @sql )
-- --------------------------------------
-- DDL--数据定义语言
-- DML--数据操作语言
-- 数据库定期备份
if day ( current_timestamp ) = 9
begin
print ' 今天是一个月的第一天,数据库备份 '
print ' 开始完全备份 '
backup database dbtest to disk = ' E:\backup\backup_dbtest_full.bak ' with init;
print ' 完全备份成功 '
end
else
begin
print ' 今天是一个月的最后一天,数据库备份 '
print ' 开始差异备份 '
backup database dbtest to disk = ' E:\backup\backup_dbtest_diff.bak ' with differential;
print ' 差异备份成功 '
end
-- ----------------------------------------------------------
use tempdb;
if object_id ( ' dbo.Orders ' , ' u ' ) is not null drop table dbo.Orders;
create table dbo.Orders
(
orderid int not null
constraint pk_order primary key ,
orderdate datetime not null
constraint def_orderdate default ( current_timestamp )
)
-- --------------子查询--------------------------------------------
-- 子查询分为1-独立子查询2-相关子查询
-- 返回结果可以是一个单独的值(标量)或者多个值或者整个表变量
declare @maxid as int = ( select max (orderid) from Sales.Orders);
select orderid,orderdate,empid,custid
from Sales.Orders
where orderid = @maxid ;
-- sql2005
select orderid,orderdate,empid,custid
from Sales.Orders
where orderid = ( select max (orderid) from Sales.Orders);
-- 对于有效的标量子查询,它的返回值不能超过一个,如果标量子查询返回了多个值,在运行时可能会失败。
-- 比如
select orderid
from Sales.Orders
where empid = ( select E.empid from HR.Employees as E where E.lastname like N ' B% ' )
select E.lastname from HR.Employees as E
-- 因为恰巧该表中只有一个人的名字是以B开头的所以,sql会认为右边子查询是标量值
-- 假如右边表达式没有返回任何值,那么两者比较得出的结果是NULL,而与NULL比较的结果都为UNKNOW,所以不会返回任何值
-- 当然上面的查询还可以用联结查询
select O.orderid
from HR.Employees as E
join Sales.Orders as O
on E.empid = O.empid
where E.lastname like N ' D% '
-- 独立多值子查询IN------
-- <标量表达式> IN <多值子查询>
-- 用某表的偶数行数据填充tempdb
use tempdb
select *
into dbo.tempdb
from TSQLFundamentals2008.Sales.Orders
where orderid % 2 = 0 ;
select * from tempdb;
-- 返回tempdb中介于min(orderid)与max(orderid)并且不在表中的orderid
select n
from tempdb
where n between ( select min (O.orderid) from tempdb as O) and ( select max (E.orderid) from tempdb as E)
and n not in ( select orderid from tempdb);
-- -----------------------游标使用-------------------------------------------
-- 游标通常步骤
-- 1.在某个查询的基础上声明游标
-- 2.打开游标
-- 3.从第一个游标记录中把列值提取到指定的变量
-- 4.当还没有超出游标最后一行时,(@@fetch_status函数返回值为0),循环遍历游标记录,在每一次遍历中,从当前游标记录把列
-- 值提取到指定的变量,再为当前执行相应的处理
-- 5.关闭游标
-- 6.释放游标
use TSQLFundamentals2008
declare @result table
(
custid int ,
ordermonth datetime ,
qty int ,
runqty int ,
primary key (custid,ordermonth)
);
declare
@custid as int ,
@prvcustid as int ,
@ordermonth as datetime ,
@qty as int ,
@runqty as int ;
declare c cursor fast_forward for
select custid,ordermonth,qty
from Sales.CustOrders
order by custid,ordermonth;
open c
fetch next from c into @custid , @ordermonth , @qty ;
select @prvcustid = @custid , @runqty = 0 ;
while @@fetch_status = 0
begin
if @custid <> @prvcustid
set @runqty = @runqty + @qty ;
insert into @result values ( @custid , @ordermonth , @qty , @runqty );
fetch next from c into @custid , @ordermonth , @qty ;
end
close c;
deallocate c;
select custid, convert ( varchar ( 7 ),ordermonth, 121 ) as ordermonth,qty,runqty
from @result
order by custid,ordermonth;
-- -----------------------------------------
-- 1)接受数据导入的表已经存在。
insert into t1 select * from OPENROWSET ( ' MICROSOFT.JET.OLEDB.4.0 ' ,
' Excel 5.0;HDR=YES;DATABASE=c:\\test.xls ' ,sheet1$);
-- 2)导入数据并生成表。
select * into t1 from OPENROWSET ( ' MICROSOFT.JET.OLEDB.4.0 ' ,
' Excel 5.0;HDR=YES;DATABASE=c:\\test.xls ' ,sheet1$);
--
-- 3) 导入Excel中指定的列到数据库表中指定的列。
INSERT INTO t1(a1,a2,a3) SELECT a1,a2,a3 FROM OPENROWSET ' MICROSOFT.JET.OLEDB.4.0 ' , ' Excel5.0; HDR=YES; DATABASE=c:\\test.xls ' ,sheet1$);
--
-- 需要注意的地方。
-- 1)外围应用配置器的设置。
-- 从“功能外围应用配置器”中选择“启动 OPENROWSET 和 OPENDATASOURCE 支持”选项。
-- 2)关闭Excel表。
-- 如果在导入时要导入的Excel表格处于打开状态,会提示:
-- “无法初始化链接服务器 "(null)" 的 OLE DB 访问接口 "microsoft.jet.oledb.4.0" 的数据源对象。”
-- 3)导入数据时,Excel的首行会作为表头,若导入到已存在的数据库表,则忽略首行。
-- ------------------------------------直接从数据库将表导出到EXCEL-------------------------------------------------------------
EXEC master..xp_cmdshell ' bcp master.dbo.t2 out d:\Temp.xls -c -q -S"127.0.0.1" -U"sa" -P"123456" '
-- 参数:S 是SQL服务器名;U是用户;P是密码
-- 查询优化------------------------------------------
set nocount on ;
use master;
if db_id ( ' performance ' ) is null
create database performance;
go
use performance;
go
-- 创建填充的数字辅助表
set nocount on ;
if object_id ( ' dbo.nums ' , ' u ' ) is not null
drop table nums;
create table dbo.nums(n int not null primary key );
declare @max as int , @rc as int ;
set @max = 1000 ;
set @rc = 1 ;
insert into dbo.nums(n) values ( 1 );
while @rc * 2 <= @max
begin
insert into dbo.nums(n) select n + @rc from dbo.nums;
set @rc = @rc * 2 ;
end
insert into dbo.nums(n) select n + @rc from dbo.nums where n + @rc <= @max ;
go
-- 如果数据表存在,则先删除
-- ----------------------------------------------------------
use insideTSQL2008;
set nocount off ;
select orderid,custid
from sales.orders
where orderid = ( select max (orderid) from sales.orders ); -- 取出orderid最大的订单信息(标量子查询)
-- -----相关子查询,返回每个客户最大的订单信息
select orderid,custid
from sales.orders as T1
where orderid = ( select max (orderid) from sales.orders as T2 where T1.custid = T2.custid)
order by custid;
-- -在期待多个值的地方可以使用多值子查询,返回下过订单的客户
select custid,companyname
from sales.customers
where custid not in ( select custid from sales.orders);
-- 在期待表的地方还可以使用 表值子查询 或 表表达式
-- 查询每个订单年份返回最大的订单ID
select * from sales.orders;
select * from sales.customers;
select order_year, max (orderid) as max_orderid
from ( select orderid, year (orderdate) as order_year from sales.orders) as T1
group by order_year;
-- 子查询可以按两种方式进行分类,按期望值的数量可以分为标量子查询,多值子查询
-- 按子查询对外部的依赖,分为独立子查询,相关子查询,标量子查询和多值子查询既可以是独立子查询,也可以是相关子查询
-- 查询由每个美国雇员至少为其处理过的一个订单的所有客户
-- 假设知道美国雇员的empid是1,2,3,4,8
-- (1)
select custid
from sales.orders
where empid in ( 1 , 2 , 3 , 4 , 8 )
group by custid
having count ( distinct empid) = 5 ;
-- (2)
select custid
from sales.orders
where empid in ( select empid from hr.employees where country = ' usa ' )
group by custid
having count ( distinct empid) = 5
-- 返回每个月最后实际订单日期发生的订单
select orderid,custid,empid,orderdate
from sales.orders
where orderdate in (
select max (orderdate)
from sales.orders
group by year (orderdate), month (orderdate));
-- 相关子查询,是引用了在外部查询中出现的列的子查询,从逻辑上讲,子查询会为外部查询的每一行进行一次计算。
-- 决胜属性(tiebreaker),是一个属性或属性列表,可以惟一的对元素进行排名
-- 先创建2张表
use master;
if db_id ( ' DbTest ' ) is not null drop database DbTest;
create database DbTest;
go
use DbTest;
go
-- 创建Customers表
create table Customers
(
custid INT NOT NULL IDENTITY ,
companyname NVARCHAR ( 40 ) NOT NULL ,
country NVARCHAR ( 15 ) NOT NULL ,
constraint pk_customer primary key (custid)
);
-- 创建Orders表
CREATE TABLE Orders
(
orderid INT NOT NULL IDENTITY ,
custid INT NULL ,
CONSTRAINT PK_Orders PRIMARY KEY (orderid),
CONSTRAINT FK_Orders_Customers FOREIGN KEY (custid)
REFERENCES Customers(custid),
);
set identity_insert Customers on ;
INSERT INTO Customers(custid, companyname,country)
VALUES ( 1 , N ' 大众 ' , N ' 中国 ' );
INSERT INTO Customers(custid, companyname,country)
VALUES ( 2 , N ' 宝马 ' , N ' 美国 ' );
INSERT INTO Customers(custid, companyname,country)
VALUES ( 3 , N ' 奔驰 ' , N ' 中国 ' );
INSERT INTO Customers(custid, companyname,country)
VALUES ( 4 , N ' 奇瑞 ' , N ' 德国 ' );
INSERT INTO Customers(custid, companyname,country)
VALUES ( 5 , N ' 福特 ' , N ' 美国 ' );
set identity_insert Customers off ;
set identity_insert Orders on ;
-- custid代表员工号
INSERT INTO Orders(orderid, custid)
VALUES ( 1 , 1 );
INSERT INTO Orders(orderid, custid)
VALUES ( 2 , 2 );
INSERT INTO Orders(orderid, custid)
VALUES ( 3 , 3 );
INSERT INTO Orders(orderid, custid)
VALUES ( 4 , 4 );
INSERT INTO Orders(orderid, custid)
VALUES ( 5 , 5 );
-- 查看表的数据
select custid,companyname,country from Customers;
select orderid,custid from Orders;
-- 插入数据成功
-- 咱们回到正题,比较Exists与in,not exists与 not in
-- 查询来自中国,而且下过订单的所有客户
select custid,companyname
from Customers as C
where country = N ' 中国 '
and exists ( select * from Orders as O where O.custid = C.custid);
-- 返回
-- custid companyname
-- 1 大众
-- 3 奔驰
-- 外部查询返回来自中国的客户信息,对于这个客户,exists谓词在Orders表查找是否至少存在一个与外部客户行信息相同的custid订单行
-- 用IN查询刚刚的需求
select custid,companyname
from Customers as C
where country = N ' 中国 '
and custid in ( select custid from Orders);
-- 结果跟上面的返回一样的值
-- 下面的知识点我们需要认识到:
-- 当列表中有NULL时,in实际会产生一个UNKNOWN的结果,例如 a in(d,b,null)的结果是UNKNOWN,而a not in (d,b,null)返回的是not unknowd仍然是unknowd
-- 而not in与not exists则结果会很不同,例如a in(a,b,null)返回的是TRUE,而a not in(a,b,null)返回的肯定是not true即为false
-- 有了上面的认识,好继续开工了....
-- 我们现在向Orders表插入一行数据(6,null)
set identity_insert Orders on ;
insert into Orders(orderid,custid) values ( 6 , null );
set identity_insert Orders off ;
set identity_insert Customers on ;
insert into Customers(custid,companyname,country) values ( 7 ,N ' 雷克萨斯 ' ,N ' 美国 ' );
set identity_insert Customers off ;
select * from Orders;
select * from Customers;
-- 合并字符串
select ' , ' + companyname from Customers where country = N ' 中国 '
for xml path( '' )
-- 假设现在要返回来自美国且没有订单的客户
select custid,companyname
from Customers as C
where country = N ' 美国 '
and not exists ( select * from Orders as O where O.custid = C.custid );
-- 返回
-- custid companyname
-- 7 雷克萨斯
-- 我们再用IN方法
select custid,companyname
from Customers as C
where country = N ' 美国 '
and custid not in ( select custid from Orders);
-- 返回的结果为空!!!
-- 为什么呢??
-- 因为还记得我们刚插入的一行数据中custid为null么,这就是问题所在
-- not in (select custid from Orders)返回的实际是unknown,所以返回结果集为空,除非你显示的规定custid不能为空
-- 下面是正确的解决方法
select custid,companyname
from Customers as C
where country = N ' 美国 '
and custid not in ( select custid from Orders where custid is not null );
-- 返回
-- custid companyname
-- 7 雷克萨斯
-- 所以在含有NULL值的列的时候,就要小心了,not exists与not in在逻辑上是不等价的
use tempdb
go
if object_id ( ' dbo.sales ' ) is not null
drop table dbo.sales;
go
-- 无法绑定由多个部分组成的标识符 "dbo.sales"?
create table sales
(
empid varchar ( 10 ) not null primary key ,
mgrid varchar ( 10 ) not null ,
qty int not null
)
delete from sales;
insert into sales(empid,mgrid,qty)
select
' B ' , ' X ' , ' 300 '
union
select
' C ' , ' Z ' , ' 300 '
union
select
' D ' , ' X ' , ' 200 '
union
select
' E ' , ' Z ' , ' 300 '
union
select
' F ' , ' Y ' , ' 150 '
union
select
' G ' , ' Y ' , ' 300 '
union
select
' H ' , ' Z ' , ' 240 '
union
select
' I ' , ' Y ' , ' 300 '
union
select
' J ' , ' Z ' , ' 350 '
union
select
' K ' , ' Z ' , ' 300 ' ;
select * from sales;
-- row_number()函数编号
select empid,qty,mgrid,
row_number() over ( partition by mgrid order by qty) as rowrank
from sales
order by qty;
-- 用基本方法计算行号(列的值不为NULL)
select empid,( select count ( * ) from sales s where s.empid < ss.empid) + 1 as rowrank from sales ss order by empid;
-- 更新某列的值为null
update sales set empid = null where empid = N ' F ' ;
-- select 字段 into 表 from 表
use tempdb;
if object_id ( ' tempdb..#myshippers ' ) is not null
drop table #myshippers;
select shipperid,companyname,phone
into #myshippers
from InsideTSQL2008.sales.shippers;
-- 如果需要为某个表创建应急的空副本,使用select into 很容易就可以得到它,只需提交以下语句
select * into target_table from source_table where 1 = 2 ;
-- 创建名为MyOrders的表
select *
into MyOrders
from InsideTSQL2008.sales.orders
where 1 = 2 ;
-- OUTPUT子句
-- 支持output子句有INSERT,DELETE,UPDATE,MERGE,可以引用特殊的inserted,deleted表
-- SCOPE_IDENTITY()函数可以返回当前范围内会话最后生成的标识值
-- @@IDENTITY 中包含语句生成的最后一个标识值,如果语句未影响任何包含标识列的表,则 @@IDENTITY 返回 NULL
--
use tempdb;
if object_id ( ' CustomersDim ' ) is not null drop table CustomersDim;
go
create table CustomersDim
(
keycol int not null identity primary key ,
custid int not null ,
companyname nvarchar ( 40 ) not null
)
-- @@rowcount返回受上一语句影响的行数。 如果行数大于 20 亿,请使用 ROWCOUNT_BIG
-- 声明一个表变量
declare @NewCusts table
(
custid int not null primary key ,
keycol int not null unique
)
insert into CustomersDim(Custid,companyname)
output inserted.custid,inserted.keycol
into @NewCusts -- 表变量
-- output inserted.custid,inserted.keycol
select custid,companyname
from InsideTSQL2008.Sales.Customers
where country = N ' UK ' ;
select * from @NewCusts ;
select @@identity ;
-- 谨慎使用,现在我们想下,假设上面表 A 和表 B 都有IDENTITY自增域,那么我们在表 A 插入一条数据后,
-- 使用了 SELECT @@IDENTITY 输出时,输出的到底是 A 还是 B 的自增域的值呢?
-- 答案很明显,是谁最后插入就输出谁,那么就是 B 了。
-- 于是,我本意是想得到 A 的自增域值,结果得到了 B 的自增域值,一只 BUG 随之诞生,搞不好还会影响到整个系统数据的混乱。
-- 因此,对于这种情况,建议大家慎用 @@IDENTITY,而尽量采用 SCOPE_IDENTITY() 函数替换之。SCOPE_IDENTITY() 也是得到最后一条自增域的值,
-- 但是它是仅限在一个操作范围之内,而@@IDENTITY 是取全局操作的最后一步操作所产生的自增域的值的。
select scope_identity ();
-- 从表中删除大量数据,并避免日志爆炸式增长和锁升级
-- 假如要删除2006年之前所有订单
-- 那我们采取分批方法,每次删除5000行
while 1 = 1
begin
delete top ( 5000 ) from LargeOrders where orderdate < ' 20060101 ' ;
if ( @@rowcount < 5000 ) break ; -- 如果没有5000行,则循环一次就跳出循环
end ;
-- 返回一个随机的数
-- select N'随机'+right('000000000'+cast(1+ abs(checksum(newid())) as varchar(10)),10) as 随机值;
-- 5分钟内向某表随机插入值
use tempdb;
if object_id ( ' randtable ' ) is not null drop table randtable;
go
create table randtable
(
ID int not null identity primary key ,
msg varchar ( max ) not null
)
declare @msg as varchar ( max );
declare @now as datetime ;
set @now = current_timestamp ;
while 1 = 1 and datediff (second, @now , current_timestamp ) < 300
begin
set @msg = N ' 随机 ' +right ( ' 000000000 ' + cast ( 1 + abs (checksum( newid ())) as varchar ( 10 )), 10 );
insert into randtable(msg) values ( @msg );
end
-- select @@rowcount--返回影响行数
select * from randtable;
select ( max (ID) - min (ID)) as N ' 总行数 ' from randtable;
select max (ID) as N ' 最大ID ' from randtable; --
select min (ID) as N ' 最小ID ' from randtable; --
-- 比较几种删除数据的执行效率
-- 我们一分钟内随机插入数据
delete from randtable; -- 用这种直接删除数据方式,删除415850条数据 用时52秒:
-- 删除5686094条数据 用时29秒;
-- 删除7679924条数据 用时19秒;
-- 删除11248379条数据 用时2秒;
-- 删除10803495条数据 用时2秒;
-- 下面我们采取另一种方式删除,即分批删除
while 1 = 1
begin
delete top ( 5000 ) from randtable -- where ID<19061681;
-- if(@@rowcount<5000) break;--如果没有5000行,则循环一次就跳出循环
end ;
-- 比较现在时间与之后之间的差值
datediff (second, @now , current_timestamp );
-- 更新表结构
alter table tablename
add constraint pk_name primary key (columnname);
-- 将空值转换为实际值
-- coalesce(comm,0)
-- 与COALESCE(expression1,...n) 的功能与以下 CASE 表达式相同:
--
-- CASE
--
-- WHEN (expression1 IS NOT NULL) THEN expression1
--
-- WHEN (expression2 IS NOT NULL) THEN expression2
--
-- ...
--
-- ELSE expressionN
--
-- END 类似
-- 利用case指定order by的列动态排序
select ename,sal,job,comm
case when job = ' saleman ' then comm else sal end as ordered
from emp
order by 5 ;
-- UNION ALL(包括重复行)
-- 将多个来源的行组合起来,放到一个结果集,所有select列表的项目数
-- 和对应项目的数据类型必须要匹配
-- UNION(不包括重复行)
-- 创建数字辅助表(Nums)
if object_id ( ' Nums ' ) is not null drop table Nums;
go
create table Nums(n int not null primary key );
declare
@max as int ,
@rc as int ;
set @max = 10000 ;
set @rc = 1 ;
insert into Nums values ( 1 );
while @rc * 2 <= @max
begin
insert into Nums select n + @rc from Nums;
set @rc = @rc * 2 ;
end
insert into Nums
select n + @rc from Nums where n + @rc <= @max ;
-- 创建一个“天”表,把Customers表与Employees表生成雇员-客户-每天组合
select custid,empid, dateadd ( day ,n - 1 , ' 20090101 ' ) as orderdate ,row_number() over ( order by ( select 0 )) as orderid
from sales.customers
cross join HR.employees
cross join Nums
where n < 31 ;
-- 对于每个订单,计算订单价格占总价格的百分比,以及它与所有订单平均价格的差额
drop table Myordervalues
select * into MyorderValues
from sales.Ordervalues;
alter table myordervalues
add constraint pk_myordervalues primary key (orderid);
create index idx_val on myordervalues(val);
select orderid,custid,val , cast (val / ( select sum (val) from myordervalues) * 100 as numeric( 5 , 2 )) as pct,
cast (val - ( select avg (val) from myordervalues) * 100 as numeric( 12 , 2 )) as diff
from myordervalues;
-- 内联结(inner)
-- 关于ON与where,如果一个查询既包含ON,又要包含where字句,逻辑上他们会依次应用,除了一个
-- 外,在Inner Join的on字句中指定逻辑表达式,还是在where字句中指定逻辑表达式,没有任何区别,因为
-- 两个步骤没有中间步骤会添加外部行。
-- 这个例外,就是当指定了group by all选项时,group by all会把where字句过滤掉的组再添加到结果集
-- 但不会添加ON字句过滤掉的组。
-- 如何规范的去放置逻辑条件呢?
-- 参考:把表之间匹配属性的筛选器应该位于ON字句中,而只对一个表的属性进行筛选的筛选器应该位于在where字句中
-- 例如:
select c.custid,companyname,orderid from sales.customers as c,sales.orders as o on c.custid = o.custid and country = N ' USA ' ;
-- 外联接(OUTER)
-- 外联接用于返回两个表中按一定条件匹配的行,以及“保留”表中不能被匹配的行
-- 可以用left,right,full,关键字保留表,left把坐标标记为保留表,right把右表标记为保留表,full把两个表都标记为保留表
-- 外联接有三个阶段:笛卡尔积-》ON筛选器-》添加外部行,对于保留表中未找到匹配的行,将作为外部行添加到结果集,并用
-- null作为相应非保留表属性的取值
-- 以下查询返回客户及其订单ID
select c.custid,o.orderid into #tb
from sales.Customers as c
left outer join sales.orders as o
on c.custid = o.custid;
-- 返回订单是NULL的客户行
select * from #tb where orderid is null ;
-- 关键字outer是可选的,因为使用left,right,full就隐含着这是一个外联接,通常使用
-- 内联接不加关键字inner,而使用外联接通常加上关键字outer
-- 其他联接
-- 自联接(是在同一个表的两个实例之间进行的联接)
-- 以下是一个简单的自联接例子,对Employees表的两个实例进行联接,一个代表雇员(E),另一个代表经理(M)
-- 当联接同一个表的两个实例时,必须至少为其中一个表应用别名,为每一个实例,提供惟一的一个名称
select E.firstname,E.lastname as emp,
M.firstname,M.lastname as mgr
from HR.Employees as E left outer join HR.Employees as M
on E.mgrid = M.empid;
-- 不等联接
-- 等值联接是联接条件基于等号(=)运算符的联接,不等联接的联接条件中包含的是除等号以外的其他运算符
-- 存储过程
-- o(∩_∩)o ,终于到存储过程这一环节了,不容易啊....
-- 存储过程是把一个或多个T-SQL语句组合到一个逻辑单元,在sqls中保存为一个对象,当首次执行时,sqlserver创建执行计划
-- 并把它存在计划内缓存中,然后进行重用,重用计划使得存储过程提供了可靠的性能
-- 优点:
-- (1)提升应用程序的可支持性
-- (2)减少网络流量
-- (3)促进代码可复用性
-- (4)淡化数据的获取方式
-- (5)查询时间比较稳定
-- (6)有效防止SQL Injection
-- (7)能作为控制层,控制一定的权限
-- 基本语法
-- 没有参数的存储过程
-- create procedure [schema_name.] procedure_name--是架构和存储过程的名字
-- as {<sql_statement>[...n]}--主体
-- 创建一个查询master数据库的存储过程
use master
go
create procedure select_master
as
select * from spt_values;
go
-- 执行存储过程
exec select_master; -- 不需要加exec procedure proc_name
-- go关键字用于标记存储过程结束
-- 在创建存储过程的时候,会检查sql语法的正确性,不会去检查引用表是否存在,这意味着你可能引用了不存在
-- 的表,并且直到运行的时候才报错,这叫做延迟名称解析
-- 创建带参数的存储过程,一个存储过程可以传入2100个参数
-- 语法如下
-- create {proc|procedure} [schema_name] procedure_name[;number]
-- [{@parameter[type_shacema_name.]data_type}[varying][=default][out|output][readonly]][,...n]
-- [with <procedure_option>[,...n]]
-- [for replaction]
-- as {<sql_statement>[;][...n]|<method_specifier>}
-- 参数以@为前缀后面是参数的数据类型和可选的默认值
--
create procedure shoppingitem
( @shoppingcartid nvarchar ( 50 ),
@quantity int = 1 ,
@productid int )
as
if exists ( select * from Sales.ShoppingCartItem
where shoopingcartid = @shoppingcartid
and productid = @productid )
begin
update sale.shoppingcartitem
set quantity = @quantity
where shoppingcartid = @shoppingcartid and productid = @productid
print ' updated.... '
end
else
begin
insert sale.shoppingcartitem(shoppingcartid,productid,quantity) values ( @shoppingcartid , @productid , @quantity )
print ' inserted..... '
end
go
-- 执行,传入三个参数
exec shoppingitem ' 1000 ' , 2 , 316
-- output参数
-- 该参数是把信息返回给存储过程调用者,无论是另外的存储过程或即席调用(嵌入程序中sql语句)
-- 例子
-- 创建一个返回某个组的部门列表
create procedure seldepartment
@groupname nvarchar ( 50 ),
@deptcount int putput
as
select [ name ]
from hr.department
where groupname = @groupname
order by [ name ]
select @deptcount = @@rowcount
go
-- 执行
declare @deptcount int
exec deldepartment ' hr ' , @deptcount output
-- 更新存储过程
alter procedure ....
-- 删除存储过程
drop procedure ....
-- 创建自动执行的存储过程
-- 每当重启sql时,自动向一张表中插入重启的时间
use master
go
create table sqlstartlog
(
startid int identity ( 1 , 1 ) primary key not null ,
startdatetime datetime not null
)
go
-- 创建一个存储过程向表中插入值
create procedure insertstartlog
as
insert sqlstartlog(startdatetime) values ( getdate ())
go
-- 设置自动执行的存储过程
exec sp_procoption @ProcName = ' insertstartlog ' , @OptionName = ' startup ' , @OptionValue = ' true ' ;
-- 重启后,查看insertstartlog
select * from sqlstartlog;
-- 如果需要禁用
exec sp_procoption @ProcName = ' insertstartlog ' , @OptionName = ' startup ' , @OptionValue = ' false ' ;
-- 在这个更总sql server启动的例子中,存储过程必须创建在master数据库中
-- 加密
-- 只需要在创建存储过程的名字后面加入with encryption
-- 查看存过程
alter procedure sampleencryption
with encryption
as
set nocount on -- 使不提示受T-SQL语句影响的行数,@@rowcount不受其影响
select * from sqlstartlog
go
--
exec sp_helptext sampleencryption;
-- 使用execute as来指定过程的安全上下文
-- with execute as 关键字允许你指定存储过程执行所在的安全上下文,覆盖存储过程调用者的默认安全
-- 概念:权链
-- 当创建一个对象(例如存储过程,或者视图),并将其用来对另外一个数据库对象进行Insert,Delete,Select,Update对象的时候
-- 就出现了“所有权链”,如果存储过程对象的架构和它引用对象的架构一样,sqlserver只会检查存储过程调用者是否有对存储过程execute的
-- 权限
-- 重新编译与缓存
-- 存储过程的性能优势主要是在重用计划
-- 当存储过程的计划自动或先是重建的时候就会发生重新编译,当存储过程中引用的基础表或其他对象
-- 发生变化后,存储过程就会在其执行期间自动重新编译。计划使用的索引发生变动或者存储过程引用的表键发生了大量的更新也可能引起重新编译
-- sql server存储过程使用的是语句级别的重新编译,能够减小负载。
-- 查询缓存数量
select count ( * ) ' CachedPlansBefore '
from sys.dm_exec_cached_plans;
-- 清空缓存
dbcc freeproccache;
-- 注意事项
-- 不能在一个存储过程中删除另一个存储过程,只能调用另一个存储过程
-- 存储过程常用命令
-- 显示所有存储过程的基本信息
show procedure status;
-- 显示使用的时间
declare @BeginDate datetime
set @BeginDate = getdate ();
begin
end
SELECT BeginDate = @dt , EndDate = GETDATE (),
Second = DATEDIFF (Second, @dt , GETDATE ());
-- 生成多少万条不重复的n位数字
use tempdb
go
-- 创建测试表
create table testtable(id char ( 8 ) );
-- 创建用于自动过滤重复值的唯一索引
create unique index ui_tb on testtable(id)
with IGNORE_DUP_KEY
go
-- 测试数据插入的时间
declare @dt datetime
set @dt = getdate ()
set nocount on ;
declare @row int
set @row = 100000
while @row > 0
begin
raiserror ( ' need %d rows ' , 10 , 1 , @row ) with nowait
set rowcount @row
insert testtable select id =right ( 100000000 + convert ( bigint , abs (checksum( newid ()))), 8 )
from syscolumns c1,syscolumns c2 -- syscolumns为每个表和视图中的每列返回一行,并为数据库中的存储过程的每个参数返回一行。
set @row = @row - @@rowcount ;
end
select BeginDate = @dt ,EndDate = getdate (),Second = datediff (Second, @dt , getdate ())
go
drop table testtable;
-- -------------------------------------------------
use tempdb
go
-- 我写的这种只有5秒左右时间,而上面却有40几秒
-- 创建测试表
create table testtable(id int identity ( 1 , 1 ) not null ,num char ( 8 ) );
-- 创建用于自动过滤重复值的唯一索引
create unique index ui_tb on testtable(num)
with IGNORE_DUP_KEY
go
-- 测试数据插入的时间
declare @dt datetime
set @dt = getdate ()
set nocount on ;
declare @row int
set @row = 100000
declare @xx char ( 8 )
while @row > 0
begin
set @xx =right ( 100000000 + convert ( bigint , abs (checksum( newid ()))), 8 );
-- print @xx;
insert testtable(num) values ( @xx )
set @row = @row - @@rowcount ;
end
select BeginDate = @dt ,EndDate = getdate (),Second = datediff (Second, @dt , getdate ())
select count ( * ) from testtable;
go
drop table testtable;
select * from dbo.testtable
-- 这是第二次温习not in与exists ,not exists了
use tempdb;
go
if object_id (N ' test ' ) is not null
drop table test
create table test
(
ID int not null identity ( 1 , 1 ) primary key ,
depno char ( 10 )
)
insert into test(depno) values ( 20 );
insert into test(depno) values ( 10 );
insert into test(depno) values ( 40 );
insert into test(depno) values ( 30 );
insert into test(depno) values ( null );
create table test2
(
ID int not null identity ( 1 , 1 ) primary key ,
depno char ( 10 )
)
insert into test2(depno) values ( 10 );
insert into test2(depno) values ( 100 );
insert into test2(depno) values ( 20 );
select * from test;
select * from test2;
select t.depno from test t where t.depno not in ( select t2.depno from test2 t2 )
-- 下面查询查不到任何值,因为子查询中depno含有null值,并且是用not in判断
select t2.depno from test2 t2 where t2.depno not in ( select t.depno from test t )
-- 采用exists
select t2.depno from test2 t2 where not exists ( select null from test t where t.depno = t2.depno )
ESCAPE
-- 如果要以文本的形式搜索,而不是被sql解释为通配符,可以使用escape关键字
where description like ' %/%% ' escape ' / ' -- 搜索与“%”匹配的
create table a (name varchar ( 10 ))
go
insert into a select ' 11%22 '
union all select ' 11%33 '
union all select ' 12%33 '
go
select * from a WHERE name LIKE ' %/%33 ' ESCAPE ' / ' -- 指定用'/'符号来说明跟在其后面的通配符字符为普能字符。(第二个%是字符不是通配符来的)
go
drop table a
-- 结果为:
-- name
-- --------
-- 11%33
-- 12%33
-- 再来一个
SELECT * FROM finances WHERE description LIKE ' gs_ ' ESCAPE ' s '
-- 意思就是: 比如,我们要搜索一个字符串 "g_" ,如果直接 like "g_",那么 "_"的作用就是通配符,而不是字符,结果,我们会查到比如 "ga","gb","gc",而不是我们需要的 "g_". 用 LIKE 'gs_' ESCAPE 'S' 's'表示特殊用法标志
-- 声明变量
-- 在sql2005不允许声明变量后紧接着就赋值
-- 而在sql2008可以省略set关键字直接赋值
use InsideTSQL2008
go
select top 10 percent * from MyorderValues; -- 返回10%的行
-- 建议
-- 避免使用不兼容的数据类型。例如float和int、char和varchar、binary和
-- varbinary是不兼容的
-- 避免使用不兼容的数据类型。例如float和int、char和varchar、binary和
-- varbinary是不兼容的。数据类型的不兼容可能使优化器无法执行一些本来可以进
-- 行的优化操作。例如:
SELECT name FROM employee WHERE salary > 60000
-- 在这条语句中,如salary字段是money型的,则优化器很难对其进行优化,因为60000
-- 是个整型数。我们应当在编程时将整型转化成为货币型,而不要等到运行时转化。
-- 尽量避免在WHERE子句中对字段进行函数或表达式操作,这将导致引擎放弃
-- 使用索引而进行全表扫描。如:
SELECT * FROM T1 WHERE F1 / 2 = 100
-- 应改为:
SELECT * FROM T1 WHERE F1 = 100 * 2
SELECT * FROM RECORD WHERE SUBSTRING (CARD_NO, 1 , 4 ) = ’ 5378 ’
-- 应改为:
SELECT * FROM RECORD WHERE CARD_NO LIKE ‘ 5378 % ’
SELECT member_number, first_name, last_name FROM members
WHERE DATEDIFF (yy,datofbirth, GETDATE ()) > 21
-- 应改为:
SELECT member_number, first_name, last_name FROM members
WHERE dateofbirth < DATEADD (yy, - 21 , GETDATE ())
-- 即:任何对列的操作都将导致表扫描,它包括数据库函数、计算表达式等等,查询
-- 时要尽可能将操作移至等号右边。
-- 尽量使用数字型字段,一部分开发人员和数据库管理人员喜欢把包含数值信
-- 息的字段
-- 设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在
-- 处理查询和连接回逐个比较字符串中每一个字符,而对于数字型而言只需要比较一
-- 次就够了。
-- 创建用','分隔的列表
select * from nums
declare @splitstring nvarchar ( 50 )
set @splitstring = '' ;
select @splitstring = @splitstring + convert ( char ( 2 ),n) + ' , ' -- 因为n是数值型,需要转换,不然就是求和了
from Nums
where n < 15 ;
select @splitstring ;
-- select into到临时表
select n into #tempdb
from nums;
select * from #tempdb;
-- 在频繁执行的查询中,发生隐式转换,将会非常影响性能例如nchar转为char
-- 复习索引
-- 索引是在表上创建的数据库对象,它可以提供到数据更快的访问,并且可以使查询执行更快
-- sql中,存储单位最小的是页,页是不可再分的,换句话说,要么整个读取,要么不读
-- 如果表上没有索引,那么就是存放在堆Heap中,即使你想找的数据就是第一项,那么sql引擎也需要进行全表扫描
-- 对于数据库检索来说,对于磁盘的IO是最消耗时间的
-- 测试sql IO次数
use tempdb
go
create table testio
(
id int ,
c1 int ,
c2 int ,
c3 int ,
c4 char ( 2 )
)
insert into testio values ( 1 , 1 , 1 , ' a ' );
insert into testio values ( 2 , 2 , 2 , ' a ' );
insert into testio values ( 3 , 3 , 3 , ' a ' );
insert into testio values ( 4 , 4 , 4 , ' a ' );
insert into testio values ( 5 , 5 , 5 , ' a ' );
insert into testio values ( 6 , 6 , 6 , ' a ' );
insert into testio values ( 7 , 7 , 7 , ' a ' );
insert into testio values ( 8 , 8 , 8 , ' a ' );
insert into testio values ( 9 , 9 , 9 , ' a ' );
insert into testio values ( 10 , 10 , 10 , ' a ' );
insert into testio values ( 11 , 11 , 11 , ' a ' );
insert into testio values ( 12 , 12 , 12 , ' a ' );
insert into testio values ( 13 , 13 , 13 , ' a ' );
insert into testio values ( 14 , 14 , 14 , ' a ' );
insert into testio values ( 15 , 15 , 15 , ' a ' );
insert into testio values ( 16 , 16 , 16 , ' a ' );
insert into testio values ( 170 , 170 , 170 , ' a ' );
-- 开启IO数量
set statistics io on ;
select * from testio where c1 = 170 ;
-- 建立索引
create index idx_testio on testio(c1);
select * from testio where c1 = 170 ;
use HR
go
select top 10 (ID) from Talent_Big;
select * from Talent_Big where ID = 480000 ;
-- 建立索引
drop index idx_talent_big on Talent_Big;
-- 理解聚集和聚集索引
--
-- 在SQL Server中,最主要的两类索引时聚集索引和非聚集索引。可以看到,这两个分类都是围绕聚集这个关键字进行的,那么首先理解什么是聚集。
--
-- 聚集在索引中的定义:
--
-- 为了提高某个属性(或属性组)的查询速度,把这个或这些属性(成为聚集码)上具有相同值的元组集合中存放在连续的物理块成为聚集。
--
-- 非聚集索引
--
-- 因为每个表只能有一个聚集索引,如果我们对一个表的查询不仅仅限于聚集索引上的字段。我们又对聚集索引列之外还有索引的要求,那么就需要非聚集索引了。
--
-- 非聚集索引,本质上来说也是聚集索引的一种,非聚集索引并不改变其所在表的物理结构,而是额外生成一个聚集索引的B树结构,但叶节点是对于其所在表的引用,
-- 这个引用分为两种,如果其所在表上没有聚集索引,则引用行号;如果其所在表上已经有了聚集索引,则引用聚集索引的页,从而实现更大限度的使用。
-- 随着数据量的增长,产生了索引碎片,很多存储的数据进行了不适当的跨页,会造成碎片
-- 这时我们可以通过重建索引来提高速度:
ALTER INDEX idx_text_tb2_EmployeeID ON test_tb2 REBUILD
--
-- 还有一种情况是,当随着表数据量的增大,有时候需要更新表上的统计信息,让查询分析器根据这些信息选择路径,使用:
--
-- UPDATE STATISTICS 表名
--
-- 那么什么时候知道需要更新这些统计信息呢,就是当执行计划中估计行数和实际表的行数有出入时:
-- 当然索引的使用也是要付出代价的:
--
-- 1、通过聚集索引的原理我们知道,当表建立索引后,就可以B树来存储数据,所以当对其进行更新插入删除,就需要页在物理上移动以调整B树,因此当更新插入删除数据时,会带来性能的下降。
-- 而对于非聚集索引,当更新表后,非聚集索引需要进行更新,相当于多更新了N(N=非聚集索引数量)个表。因此也下降了性能。
--
-- 2、通过上面对非聚集索引原理的介绍,可以看到,非聚集索引需要额外的磁盘空间。
--
-- 3、前文提过,不恰当的非聚集索引反而会减低性能。
--
-- 所以使用索引需要根据实际情况进行权衡.通常我都会将非聚集索引全部放到另外一个独立硬盘上,这样可以分散IO,从而使查询并行.
-- 视图
-- sql中分为3种视图
-- 普通视图,在数据库中不保存实际数据,只是视图定义
-- 索引视图,保存了真实的索引数据
-- 分布式分区视图,可以用UNION ALL来把多个跨两个以上sql实例的较小的表组合成一个虚拟表
-- select语句允许一个视图定义1024个列,然而,不可以在视图定义中使用某些select元素,包括into,option,compute,compute by 或者对
-- 表变量或临时表的引用。除非使用了top关键字,否则也不可以使用order by
-- 创建视图
create view view_somedefin
as
select * from testio
go
-- 查看某个视图的定义
select definition from sys.sql_modules
where object_id = object_id ( ' view_somedefin ' );
-- 删除视图
drop view view_somedefin;
-- 视图加密
create view view_somedefinencrypt
with encryption
as
select * from testio;
go
-- 此时通过sys.sql_modules系统目录视图来查看该视图的definition是null
-- 使用视图修改数据
-- 可以像普通表一样对视图进行插入,更新,删除,但是这些操作只能引用一个表中的列,而且,这些操作中引用列不能进行
-- 衍生,例如它们不能基于聚合函数被计算或受group by ,distinct 或having子句
-- 例子
create view view_production
as
select ID,proname,proprice,num,num * prpprice totalcost
from production
go
-- 以上创建了一个视图,现在向其中插入一条数据
insert view_production (proname,proprice,num,totalcost) values ( ' 可乐 ' , 2 , 10 , 20 );
-- 发生错误
-- 对视图或函数’view_production‘的更新或插入失败、因其包含派生域或常量域
-- 其实问题是出生在’totalcost‘中,它引用了两列,只要去掉插入该列的值即可
-- 创建索引视图
use InsideTSQL2008
go
create view view_Employees
with schemabinding
as
select E.empid ,E.lastname,E.firstname,E.title,E.address,E.city,O.orderdate,O.shipname
from HR.Employees E
join
Sales.Orders O
on O.empid = E.empid ;
go
-- drop view view_Employees;
set statistics io on ;
select * from view_Employees
-- 查询完毕后,我们发现empid这列是无序的,并且Orders表的逻辑读取次数为21次,Employees的逻辑读取次数2次
-- 因为empid为主键与外键,所以已经有了聚集索引,而一个表中只能有一个聚集索引,所以为视图创建非聚集索引
create nonclustered index uci_index_Employees_Orders
on Sales.Orders(orderdate)
go
-- 删除索引
drop index Sales.Orders.uci_index_Employees_Orders;
作者:大蜗牛 出处:http://HdhCmsTestcnblogs测试数据/lixiaofei/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
分类: Sql Server
标签: sql笔记
作者: Leo_wl
出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did49183