SET IDENTITY_INSERT tblName ON --开启手动插入
INSERT INTO tblName ...--insert语句要含id值,但是不能重复!!(如果tblName就1条数据,id值500,则下一个id为501)
SET IDENTITY_INSERT tblName OFF--关闭手动插入
SELECT 平均年龄=(SELECT SUM(age) FROM tlb1)/(SELECT COUNT(age) FROM tlb1)--结果是int型,如果想让结果是小数,则sum值乘以1.0即可.
SELECT Emp_No,SUM(1) a FROM PR_People Where AssessID='225' Group By Emp_No having sum(1)=1 ORDER BY Emp_No
- SELECT * FROM Base_Organize WHERE OrderID IN(1,2,3) --OR 等同于in ,但是如果OrderId是有序的则“OrderID>=1 AND OrderID<=3”(其等同于“between 1 and 3”)更高效
Where、Having“过滤”功能区别详解:行级过滤,用where。对分组后的结果集过滤,用having。具体:where语句的执行顺序在分组之前,并且where只能过滤行,不能过滤分组(where中不能使用聚合函数)。having执行在分组之后,对group分组的结果集进行过滤,having子句中可以直接使用聚合函数。
Null的处理:
- age is null 和age is not null(不能用age =null或age <>null)。
- SELECT 11+null,返回null(任何值与null计算后得到null)
- isNull(age,0)–age列用值0替换所有 NULL 条目。
- Null值并不是一个值,任何与Null比较结果一定为Null。
- 查询空值或者非空值的时候,用的是is null/is not null
- 按照ANSI SQL标准,
select * from T where Data=null 或 Data<>null
这两种方式都不返回任何行,只有Data is null
才能有返回值。实际工作中遇到如:SELECT * FROM A WHERE id NOT IN ( 2, NULL )
等同于.... WHERE a.id <> 2 AND a.ID <> NULL
而NULL值不能参与比较运算符,即not in子查询中有null值的时候,则不会返回数据。解决办法是子查询中排除NULL值。
1-from,2-on,3-join,4-where,5-group by,6-with cube或with rollup,7-having,8-select,9-distinct,10-order by,11-top
- where name like '安__'--返回所有安开头的3个字的姓名
where name like '张[0-9]妹'----返回所有张和妹中间是数字('张[a-z]妹'、'张[0-9a-z]妹'(中间可是数字或字母)、'张[^0-9]妹'(中间不能是数字就行))
where name like '%[%]%'——返回所有带‘%’的名字,默认放到[]中实现转义。自定义转义 select * from tbl where name like '%/[/]%' ESCAPE '/'
SELECT 1+'11'--自动类型转换
SELECT 'a'+1--转换失败
SELECT 1.1+'11'--转换失败
SELECT CAST('11' AS int) +1.1
SELECT CONVERT(INT,'11')+1.1
SELECT CONVERT(VARCHAR(10),GETDATE(),120)--日期输出格式:"yyyy-MM-dd"
SELECT CONVERT(VARCHAR(20),GETDATE(),120)--日期输出格式:"yyyy-MM-dd +时分秒"
方法1:用union、union all。
方法2:select * into tbl from student (tbl表会自动被创建,只是copy数据、列名及数据类型,对于约束不会复制)。
select * into tbl from student where 1<>1 ,只复制表结构,效率低。建议:select top 0 * into tbl from student
方法3:insert into tbl select * from student (tbl表必须提前建好)
SELECT LEN('1啊')--字符串个数:2个
SELECT DATALENGTH('1啊')--字符串磁盘中大小:3b(字节)
SELECT DATALENGTH(N'1啊')--字符串磁盘中大小:4b
SELECT LOWER('HI')--hi 转小写
SELECT UPPER('hi')--HI 转大写
SELECT LTRIM(RTRIM('== ')+LTRIM(' =='))--去掉字符串首尾空格
SELECT LEFT('abcd',2) --从左边开始截取几个字符 结果:ab
SELECT RIGHT('abcd',2) --从右边开始截取几个字符 结果:cd
SELECT SUBSTRING('abcd',2,3)--截取串,从坐标轴是2()的字符开始取3个 结果:bcd。给串画一条x轴则a对应1,轴上可以为负或0。如: SELECT SUBSTRING('abcd',-1,3)--结果是‘a’
SELECT SYSDATETIME()-- 高精度时间
SELECT DATEADD(second,20,GETDATE())--当前时间增加20 second/minute/hour/day/week/month/year
SELECT * FROM tbl WHERE DATEADD(YEAR,1,joinDate)<=GETDATE()--入职1年以上的人员
SELECT DATEDIFF(MONTH,'1987-11-14',GETDATE())--两个日期差,可知道某个人在地球上生存了多久,时间维度可为“second/minute/hour/day/week/month/year”
SELECT 工龄=DATEDIFF(DAY,JoinTime,GETDATE())/365,COUNT(1) FROM tbl GROUP BY DATEDIFF(DAY,JoinTime,GETDATE())/365--各工龄的人数
SELECT DATEPART(year,GETDATE())--取得当前日期的年部分(结果是int型) 可为“second/minute/hour/day/week/month/year” SELECT YEAR(GETDATE()) 年,month(GETDATE()) 月,day(GETDATE()) 日 --简写只有“年、月、日”
SELECT DATENAME(YEAR,GETDATE())--取得当前日期的年部分(结果是varchar型)
sql1:SELECT * FROM tbl1 ,tbl2 --结果集是返回笛卡尔积
sql2:SELECT * FROM tbl1 a inner join tbl2 b on a.id=b.tId--先得到sql1的笛卡尔积,然后筛选出符合on条件的结果并返回
int? age=txtAge.Text.Length()==0 ? null : (int?)txtAge.Text;//要给后者转为int?,因为问号表达式要求三者数据类型要一致。
string sql="insert into tbl values(@name,@age)";
SqlParameter[] pms=new SqlParameter[]{
new SqlParameter("@name",SqlDbType.NVarChar,50){Value="张三"}, //SqlDbType.NVarChar,50——必须与数据库中一致 不推荐“new SqlParameter("@name","李四"),”写法
new SqlParameter("@age",SqlDbType.Int){ Value=age==null ? DBNull.Value : (object)age} //往DB中insert值null,必须是DBNull.Value
};
int? n=null; 等价于Nullable<int> n=null;书上说值类型不能为null,int?可以原因是:它是一个结构在里面封装属性HasValue,无值在其为false,当写“if(n==null){}”等价于“if(!n.HasValue){}”,这些工作都编译器帮我们做的。
--Case搜索函数,相当于C#的if-else(条件可以是一个范围,推荐实际使用)
select 论坛头衔= case when level>=1 and level<=2 then '菜鸟' when level>=3 and level<=4 then '老鸟' else '??' end from user
--Case函数,相当于C#中的switch(判断条件只能是一个值,不推荐)
select case level when 1 then '菜鸟' when 3 then '老鸟' else '??' end as [论坛头衔] from user
唯一索引
,不允许两行具有相同的索引值,一个表可以有多。“聚集索引也叫聚簇索引
”(目录顺序与内容顺序一样。像新华字典的拼音方式查字,一个表只能一个)和“非聚集索引
”(目录顺序与内容顺序不一样。如字典的偏旁部首找字,一个表可有多个,但是要<249个)。非聚集索引,反应了从多少个角度查询数据,如图书馆,我可按书名找或按价格找等等。主键索引就是一个唯一索引,就是一个聚集索引。select * from ( select col1,col2 from #tmp) as tbl ; select * from tbl where empNo in(select empNo from pr_people where id<10); select * from tbl a where exists (select * from pr_people b where a.empNo=b.empNo and b.id<100)
我们写的所有查询都能改为“相关子查询”实现!!!解析本处的含exists语句,该SQL会扫表当扫到第一条时,exists里的sql是true第一条就会返回。 --TOP版分页
DECLARE @page int=2 --第二页内容
declare @pSize int=10--每页10条
SELECT top (@pSize) * from People where id not in(
select top (@pSize*(@page-1)) id from People order by id asc
) order by id asc
--不用not in版: select top 7 * from (select top (2*7) * from tbl order by id asc ) as t order by id desc
--ROW_NUMBER()版
DECLARE @page INT= 2; --页号
DECLARE @pSize INT= 10;--每页条数
SELECT *
FROM ( SELECT row_No = ROW_NUMBER() OVER ( ORDER BY ID ASC )--row_number--生成编号列,over--请按照id列asc排序后为其编号
,
Emp_NO
FROM B_Employee
) tbl
WHERE row_No BETWEEN ( @page - 1 ) * @pSize + 1 AND ( @page * @pSize );
DECLARE @a int
set @a=(SELECT count(*) from tbl)
SELECT @a=count(*) from tbl
print @a
--上述效果一样。
set @a=(SELECT empNo from tbl)--当返回多个值,set赋值会报错
SELECT @a=empNo from tbl --当返回多个值,select赋值会把最后一个给@a
print @a
left join中关于where和on条件的几个知识点:(筛选条件写在where还是on后面解读)
1.多表left join是会生成一张临时表,并返回给用户
2.where条件是针对最后生成的这张临时表进行过滤,过滤掉不符合where条件的记录,是真正的不符合就过滤掉。
3.on条件是对left join的右表进行条件过滤,但依然返回左表的所有行,右表中没有的补为NULL
4.on条件中如果有对左表的限制条件,无论条件真假,依然返回左表的所有行,但是会影响右表的匹配值。也就是说on中左表的限制条件只影响右表的匹配内容,不影响返回行数。
结论:
1.where条件中对左表限制,不能放到on后面
2.where条件中对右表限制,放到on后面,会有数据行数差异,比原来行数要多
--正确,但是麻烦
select * from study st left join (SELECT * FROM study_req where deleted_Mark=0) sr on st.study_id = sr.study_id where st.deleted_Mark=0
--正确???
select * from study st left join study_req sr on st.study_id = sr.study_id on sr.deleted_Mark=0 where st.deleted_Mark=0
--错误写法
select * from study st left join study_req sr on st.study_id = sr.study_id where st.deleted_Mark=0 and sr.deleted_Mark=0
--返回笛卡尔积:SELECT * FROM B_Organize a, B_Organize b
SELECT a.OrgName,b.OrgName FROM B_Organize a inner join B_Organize b on a.Org_NO=b.FatherID--返回“襄阳市,湖北省;武汉市,湖北省;宜昌市,湖北省;
CREATE TABLE [dbo].[B_Organize]
(
[Org_ID] [int] NOT NULL IDENTITY(1, 1),--表id
[Org_NO] [int] NOT NULL,--机构号
[OrgName] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NOT NULL,--组织机构名称
[Visable] [tinyint] NOT NULL,--是否显示在通讯录中
[Flag] [tinyint] NOT NULL,--是否为组织机构,1为组织机构,0为用户组
[OrderID] [int] NULL,--排序顺序
[FatherID] [int] NULL,--上一级节点ID
[FatherPath] [varchar] (50) COLLATE Chinese_PRC_CI_AS NULL,--组织机构全路径
[EffectDate] [datetime] NULL,--启用时间
[ExpiredDate] [datetime] NULL,--失效时间
[istop] [int] NULL,
[orderid_sap] [nvarchar] (50) COLLATE Chinese_PRC_CI_AS NULL
)
declare @name varchar(50)
declare @age int
或 declare @name varchar(50),@age int
PRINT @@VERSION--数据库版本,@@error,最后一次sql错误的错误号(正常返回0,错误则“<>0”),@@identity 最后一次插入的标示值
PRINT @@MAX_CONNECTIONS--数据库可同时连接的最大数
PRINT @@rowcount--上条sql影响的行数
PRINT @@SERVERNAME--服务器名称
使用while循环
declare @i int=1;
while (i<10)--括号可不写
begin
print 'hi'
set @i=@i+1
break;--跳出循环
continue;--继续下次循环
end
if语句
if @i>10
begin
print 'n大于10'
end
else if @i>5
begin
print 'n大于5'
end
else begin
print 'hi'
end
事务:事务一旦开启就两种可能"ROLLBACK,COMMIT"。 事务分类:自动提交事务
(当我们执行一条insert、update、delete,如果出错就回滚);隐式事务
(每次执行一条sql,数据库自动帮我们打开一个事务,但是需要手动提交或回滚事务);显示事务
(需要手动打开事务,手动提交或回滚事务)
BEGIN TRANSACTION --开始转账事务 或 “begin tran”
DECLARE @flag INT=0 --汇总的@@error值
UPDATE tbl SET money =money+100 WHERE id='u001'--给账号1加100元
SET @flag=@flag+@@ERROR --必须在每条sql后面进行累加操作
UPDATE tbl SET money =money-100 WHERE id='u002' --给账号2减100元,假设该人只有108元,而该表有对money检查约束(该字段最少10),故必会发生异常
SET @flag=@flag+@@ERROR
IF(@flag>0) --有一条sql语句出错,就回滚事务
BEGIN
ROLLBACK--回滚
END
ELSE BEGIN
COMMIT--没出错就提交事务
END
SET IMPLICIT_TRANSACTIONS ON--打开隐式事务
INSERT INTO tbl VALUES('12','123123')--当没写COMMIT,则此表一直被占用,此时执行select时给人感觉是耗费很久但是还是查询不到结果。
COMMIT
SET IMPLICIT_TRANSACTIONS off--关闭隐式事务
事务的ACID特性:原子性
(各步操作不可分隔,要么都执行要么都不执行)、一致性
(事务完成后所有数据一致,银行总钱数在“已有数+n次转入-n次转出”后不会发生混乱)、隔离性
(并发事务在执行时,是互相独立的别人不可干预)、持久性
(数据会被永久保存)
Trigger(触发器,枪的扳机)一个特殊的存储过程,事件触发后自动调用它。具备事务功能;它能在多表之间执行特殊业务规则。触发器是在对表进行插入、删除、改时自动执行的存储过程(它是与表相关的,是自动执行,不能直接调用)。触发器是一种高级约束,可以定义比check约束更复杂的约束。扩展:SQLSERVER的触发器
--为tbl表创建执行删除后把被删除的内容备份到备份表
CREATE TRIGGER triName_delete ON tbl
AFTER delete
AS
BEGIN
INSERT INTO tbl的备份表 VALUES('','') SELECT * FROM deleted
END
存储过程
sp_databases--返回所有数据库
sp_helpdb——列出所有数据库
sp_renamedb——更改数据库名称(sp_password--添加修改登录账户的密码)
sp_tables--返回某个库所有表(SELECT * FROM INFORMATION_SCHEMA.TABLES)
sp_columns 'pr_people'--返回某个表所有字段信息 (SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='pr_people')
select * from sysobjects where xtype='V'--库中所有的视图
sp_helptext——查看存储过程、触发器、视图的实体文本
sp_help 'pr_people'——看某个表所有信息
sp_helpconstraint 'pr_people'——看某个表约束
sp_helpindex 'pr_people'——看某个表索引
sp_stored_procedures--列出所有存储过程(含函数)
--无参存储过程
create proc usp_myProc
as begin
print 'hi'
end
-- 自己写带2个参数存储过程 exec usp_myProc 1,2 或exec usp_myProc @n1=1,@n2=2
create proc usp_myProc @n1 int=0,@n2 int--可以给默认值
as begin
select @n1+@n2
end
-- 输出参数的存储过程 适用情况:当不仅需要返回查询结果还需要返回其他值,如分页存储过程返回结果和总条数。
create proc usp_myProc @n1 int=0,@rowcount int output --输出参数关键字
as begin
select * from tbl where id=@n1
set @rowcount=(select count(*) from tbl where id=@n1)
end
--调用语句:
declare @rc int; exec usp_myProc 12,@rc output; print @rc;(下例子out与C#的out类似)
○ 有这样一个需求:创建查询机构表存储过程,传入参数机构号多个用逗号分隔如"19999999,10000104"
实现思路:
--方法1:自定义的split函数
create function [dbo].[f_split](@c varchar(2000),@split varchar(2))
returns @t table(col varchar(20))
as
begin
while(charindex(@split,@c)<>0)
begin
insert @t(col) values (substring(@c,1,charindex(@split,@c)-1))
set @c = stuff(@c,1,charindex(@split,@c),'')
end
insert @t(col) values (@c)
return
end
--调用:SELECT * FROM Organize WHERE Org_NO IN( select * from f_split('1,2,3',',') )以table形式返回分隔后值
--方法2:
DECLARE @s VARCHAR(5000);DECLARE @orgNo VARCHAR(5000)='19999999,10000104'
SET @s='SELECT * FROM Organize WHERE Org_NO IN('+@orgNo+')'
EXEC (@s)
CREATE PROCEDURE Proc_Test @INPUT int,@OUTPUT int output
AS BEGIN
SET NOCOUNT ON;--不返回受影响行数
SELECT @OUTPUT=@INPUT
RETURN @INPUT+1--注意return
END
--调用:
DECLARE @OUT int,@Rt int
EXEC @Rt=Proc_Test3 10,@OUT output
SELECT @Rt,@OUT
脏读含义
:用户A对一个资源做了修改,此时用户B正好拿到了A修改的结果,但是B拿走后A又进行了回滚,B拿到与真实的不同。通过加锁机制可以避免脏读(因为锁可以做到:用户甲查询时,则不让用户乙改变表内容(因为甲查询给表加读锁了,就不让加写锁)或用户乙改变表内容时,不让用户甲查(因为给表加写锁了,就不让加读锁)。一个表可以被多个用户同时查)。with(nolock)
作用是对查询的表不加读锁。这样好处是,它既不会被排他锁阻塞也不会去阻塞排他锁。但是会引起脏读。它允许SELECT语句读取正在修改中的数据。可提高并发时的性能,效果要在并发环境才可看到,多用在对读出的数据精度要求不高的情况下。- 锁:
- 一个表的读锁可以有多个。
- 一个表的写锁只能有一个。
- 一个表的读锁与写锁互斥。
select request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName,* from sys.dm_tran_locks where resource_type='OBJECT'
(在查询时间点数据库加锁情况的快照,列出来的不都是死锁但是死锁包含其中),然后对表解锁KILL 52
(杀掉被锁的进程ID)。(sp_lock
,SQL Server 2000就有,查看当前阻塞的数据表,以便分析程序,解决问题。)
select @@LOCK_TIMEOUT
——返回会话的锁超时时间,单位为毫秒。默认-1
,代表没有超时时间。而0
,在发生资源锁定时,不做任何等待,直接返回1222错误。SET LOCK_TIMEOUT 1800
——超时期限设置为1,800毫秒。仅对当前会话有效,对新会话无效。- 弊端:超时值设置不恰当,则SQL server会不分好坏超时就返回错误。
工具 → SQL SERVER Profiler → 跟踪属性 → “常规”选项卡,“使用模板”里选TSQL_Locks → “事件选择” 勾选“Locks”的“Deadlock graph”等有Deadlock字样的选项 → 点确定
统计结果中“EventClass”是“Deadlock graph”是以图形显示死锁的图形事件。资料1、资料2[扩展]
- 深入浅出SQL Server中的死锁, 在多并发的环境中,死锁不可避免,只能尽量的优化查询/索引/隔离等级来降低死锁发生的可能性。
- T-SQL查询进阶—理解SQL Server中的锁
- 人为模拟一个死锁——使用sys.dm_tran_locks处理死锁问题。
- DMV(Dynamic Management Views,动态管理视图)是SQL Server内核的元数据,通过对这些元数据分析,从而进行性能分析。
- 当查询在SQL Server中运行时,SQL Server会自动的将此次活动的相关信息记录下来,并且保存在内存之中,这些活动信息,就称之为:DMV。
- DMV只会在2005及以上的版本存在,2000没有引用DMV的概念。
- SQL中利用DMV进行数据库性能分析
- sql优化之(DMV)
- SQL Profiler工具简介 ,Profiler可以用自定义一个跟踪模板,更多见“跟踪模板”部分。
垂直分库:(我的理解:1个系统有1台web服务器1台DB服务器,通过加机器把库拆分成n个库,然后放到n台DB服务器上,来分摊压力。如:csdn的博文数据库、收藏夹数据库)
1、把一个数据库根据业务模块分成多个数据库。用户相关的数据放到用户数据库。订单相关数据放到订单数据库上。如果同样的用户信息重复存在于不同业务系统数据库里,则弊端是数据同步很麻烦且会发生滞后问题!!!——我2014年到2018年期间维护的多个系统都有自己的用户信息表,他们这些用户信息同步方式是先从SAP同步数据到OA然后同步其他子系统。
数据库运维人员工作时间:凌晨2、3点。数据库可在“管理> 维护计划”每隔一段时间执行分表。
[数据库集群]:读写分离。但是有数据同步问题。“一个主库,多个从库” → “多主库,多从库”。互联网应用的最好用:MySQL、oracle,最好不用SQL server。
[缓存解决,磁盘IO瓶颈] 微博先放到Redis(解决读写瓶颈),然后再用MySQL保证数据完整性。
大表数据连接查询优化 临时表
数据库中有两个大表:userInfo(100w)、orderInfo(1000w)表,需要把所有用户数据和所有订单数据关联成一起后显示,如果直接连接查询,则要从笛卡尔集条数是“100w*1000w”条筛选,查询速度会很慢。
优化思路:分别对两个单表进行查询出的结果放到内存,在内存里组装数据。两个大表不能直接连接,表上锁时间不能过长。
1、把连接查询进行分解,转成单个大表的查询最后在内存组装数据。
2、使用临时表(select * into #tbl from userInfo),解决“查询频繁造成死锁问题” tbl1 inner join tbl2 on a.id=b.tid–查询会锁tbl1和tbl2,尽量减少在原始表上加锁。用临时表来降低原始表被锁的情况。
sql优化自己总结:一个表查询执行时间不能过长也不能过频繁,要连接的表尽量少,采用临时表连接。频繁写入的表尽量小,索引尽量少(最好只有PK索引)。注意s/x锁的问题。select语句where后面常用的字段巧用索引和WITH(NOLOCK)。
[扩展] 表变量、临时表
表变量在T-SQL语句执行结束后立刻自动被清除(选中下面SQL块,执行没问题,然后在单独选中SELECT * FROM @tb1
执行就会异常)。
DECLARE @tb1 Table
(
Id int,
Name varchar(20),
Age int
)
INSERT INTO @tb1 VALUES(1,'刘备',22)
SELECT * FROM @tb1
/*临时表与join的使用*/
SELECT soh.CustomerID ,
MIN(soh.OrderDate) AS OrderDate
INTO #MinOrderDates
FROM Sales.SalesOrderHeader soh
GROUP BY soh.CustomerID;
SELECT soh.CustomerID ,
soh.SalesOrderID ,
soh.OrderDate
FROM Sales.SalesOrderHeader soh
JOIN #MinOrderDates t ON soh.CustomerID = t.CustomerID
AND soh.OrderDate = t.OrderDate
GROUP BY soh.CustomerID;
DROP TABLE #MinOrderDates;
fee
) 。扩展:如何设计合理高效的数据库