一些t-sql技巧

来源:互联网 发布:河池网络问政 编辑:程序博客网 时间:2024/05/16 05:49

    本文的源地址:  
    [1] http://ghd258.cnblogs.com/archive/2006/03/20/354147.html

    [2] http://ghd258.cnblogs.com/archive/2006/07/14/450585.html

    [3 http://ghd258.cnblogs.com/archive/2006/02/12/329288.html

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

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

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

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

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

create function fgetscript(
@servername varchar(50)     --服务器名
 ,@userid varchar(50)='sa'    --用户名,如果为nt验证方式,则为空
 ,@password varchar(50)=''    --密码
 ,@databasename varchar(50)    --数据库名称
 ,@objectname varchar(250)    --对象名
returns varchar(8000)
as
begin
 
declare @re varchar(8000)        --返回脚本
 declare @srvid int,@dbsid int       --定义服务器、数据库集id
 declare @dbid int,@tbid int        --数据库、表id
 declare @err int,@src varchar(255), @desc varchar(255--错误处理变量

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

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

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

 
if @err<>0 goto lberr

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

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

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

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

 
--print @re
 return(@re)

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


    (2)用法如下:

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


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

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


    3 分隔字符串

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

    (1) 获取元素个数的函数

 

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


    (2) 获取指定索引的值的函数

 

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


    (3) 用法如下:

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

    4  一条语句执行跨越若干个数据库

    我要在一条语句里操作不同的服务器上的不同的数据库里的不同的表,怎么办呢?
    方法一:

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

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

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

    然后:

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


    5 获取一个表中所有的字段信息

    先创建一个视图

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

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

 

    查询时:

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

 

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

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

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


    6  时间格式转换问题

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

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

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

 

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

SELECT SUBSTRING(shengri, 32AS yearSUBSTRING(shengri, 62AS month
      
SUBSTRING(shengri, 92AS day
FROM lvshi
WHERE (zhiyezheng = '139770070153')

 

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

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

 

    7 分区视图

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

 

--看下面的示例

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

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

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

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

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

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

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

--显示结果
select * from v_t
go

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

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

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

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

 


    8 树型的实现

 


--参考

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

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

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

 
return
end
go

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

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

 

    9 排序问题

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

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

    下面这句执行5次

insert t values (newid())

 

    查看执行结果

select * from t

    (1) 第一种

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

    (2) 第二种

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

    (3) 第三种

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

    (4) 第四种

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

    (5) 第五种

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

    (6) 第六种

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

    10 一条语句删除一批记录

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

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

    还有一种就是

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

    11 获取子表内的一列数据的组合字符串

    下面这个函数获取05年已经注册了的某个所的律师,唯一一个参数就是事务所的名称,然后返回zhuce字段里包含05字样的所有律师。 

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

    12 让0变成1,1变成0 

declare @a int
set @a =0 --初始为0
select @a
set @a = @a^1 --把0变成1
select @a
set @a = @a^1 --把1变成0
select @a

     13  四种方法取表里n到m条纪录

    (1) 第一种 

 如果tablename里没有其他identity列,那么:

 

select top m * into 临时表(或表变量) from tablename order by columnname -- 将top m笔插入
set rowcount n
select * from 表变量 order by columnname desc

   (2) 第二种 

 

select top n * from (select top m * from tablename order by columnname) a order by columnname desc

   (3) 第三种

 select identity(int) id0,* into #temp from tablename

 取n到m条的语句为:

 select * from #temp where id0 >=n and id0 <= m

 如果你在执行 select identity(int) id0,* into #temp from tablename这条语句的时候报错,那是因为你的DB中间的select into/bulkcopy属性没有打开要先执行:
 exec sp_dboption 你的DB名字,'select into/bulkcopy',true


  
(4) 第四种
 如果表里有identity属性,那么简单: 

select * from tablename where identitycol between n and m 


  14 快速获取表test的记录总数

select rows from sysindexes where id = object_id('test'and indid in (0,1)

  15 提取数据库内所有表的字段详细说明的SQL语句

SELECT 
(
case when a.colorder=1 then d.name else '' end) N'表名'
a.colorder N
'字段序号'
a.name N
'字段名'
(
case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then ''else '' 
end) N'标识'
(
case when (SELECT count(*
FROM sysobjects 
WHERE (name in 
(
SELECT name 
FROM sysindexes 
WHERE (id = a.id) AND (indid in 
(
SELECT indid 
FROM sysindexkeys 
WHERE (id = a.id) AND (colid in 
(
SELECT colid 
FROM syscolumns 
WHERE (id = a.id) AND (name = a.name))))))) AND 
(xtype 
= 'PK'))>0 then '' else '' end) N'主键'
b.name N
'类型'
a.length N
'占用字节数'
COLUMNPROPERTY(a.id,a.name,'PRECISION'as N'长度'
isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0as N'小数位数'
(
case when a.isnullable=1 then ''else '' end) N'允许空'
isnull(e.text,'') N'默认值'
isnull(g.[value],''AS N'字段说明' 
FROM syscolumns a 
left join systypes b 
on a.xtype=b.xusertype 
inner join sysobjects d 
on a.id=d.id and d.xtype='U' and d.name<>'dtproperties' 
left join syscomments e 
on a.cdefault=e.id 
left join sysproperties g 
on a.id=g.id AND a.colid = g.smallid 
order by object_name(a.id),a.colorder
获取表结构
[把 'sysobjects' 替换 成 'tablename' 即可] 

SELECT CASE IsNull(I.name, ''
When '' Then '' 
Else '*' 
End as IsPK, 
Object_Name(A.id) as t_name, 
A.name 
as c_name, 
IsNull(SubString(M.text1254), ''as pbc_init, 
T.name 
as F_DataType, 
CASE IsNull(TYPEPROPERTY(T.name, 'Scale'), ''
WHEN '' Then Cast(A.prec as varchar
ELSE Cast(A.prec as varchar+ ',' + Cast(A.scale as varchar
END as F_Scale, 
A.isnullable 
as F_isNullAble 
FROM Syscolumns as A 
JOIN Systypes as T 
ON (A.xType = T.xUserType AND A.Id = Object_id('sysobjects') ) 
LEFT JOIN ( SysIndexes as I 
JOIN Syscolumns as A1 
ON ( I.id = A1.id and A1.id = object_id('sysobjects'and (I.status & 0x800= 0x800 AND A1.colid <= I.keycnt) ) 
ON ( A.id = I.id AND A.name = index_col('sysobjects', I.indid, A1.colid) ) 
LEFT JOIN SysComments as M 
ON ( M.id = A.cdefault and ObjectProperty(A.cdefault, 'IsConstraint'= 1 ) 
ORDER BY A.Colid ASC


  16 SQL Server中删除重复数据的几个方法
 
   
数据库的使用过程中由于程序方面的问题有时候会碰到重复数据,重复数据导致了数据库部分设置不能正确设置。

  (1) 方法一

declare @max integer,@id integer
declare cur_rows cursor local for select 主字段,count(*from 表名 group by 主字段 having count(*> 1
open cur_rows
fetch cur_rows into @id,@max
while @@fetch_status=0
begin
select @max = @max -1
set rowcount @max
delete from 表名 where 主字段 = @id
fetch cur_rows into @id,@max
end
close cur_rows
set rowcount 0 

  (2) 方法二

  有两个意义上的重复记录,一是完全重复的记录,也即所有字段均重复的记录,二是部分关键字段重复的记录,比如Name字段重复,而其他字段不一定重复或都重复可以忽略。

  〈1〉 对于第一种重复,比较容易解决,使用

select distinct * from tableName 

  就可以得到无重复记录的结果集。

  如果该表需要删除重复的记录(重复记录保留1条),可以按以下方法删除

select distinct * into #Tmp from tableName
drop table tableName
select * into tableName from #Tmp
drop table #Tmp 

  发生这种重复的原因是表设计不周产生的,增加唯一索引列即可解决。

 〈2〉 这类重复问题通常要求保留重复记录中的第一条记录,操作方法如下

 假设有重复的字段为Name, Address,要求得到这两个字段唯一的结果集

select identity(int,1,1as autoID, * into #Tmp from tableName
select min(autoID) as autoID into #Tmp2 from #Tmp group by Name,autoID
select * from #Tmp where autoID in(select autoID from #tmp2) 

 最后一个select即得到了Name,Address不重复的结果集(但多了一个autoID字段,实际写时可以写在select子句中省去此列)

  17 查询分析器不能单步调试的的原因

  具体步骤如下:
  1、将服务器【身份验证】属性设置成【混合模式】(window与sql身份验证)
  2、在【控制面板】中打开【服务】将【MSSQLSERVER】服务打开【属性】,选择【登录】页面,将登录身份设置成服务器本地帐号和该帐号密码,如administrator,密码123;
  3、重新启动sqlserver服务,此时的服务指的是【SQL服务管理器】中的SQL SERVER服务;假设【帐号】设置为administrator
  此时达到的效果是:服务器本地帐号administrator与客户端上的administrator(并且该帐号的密码要与服务器密码相同)可以通过【查询分析器】进行调试;
  如果想让【其他帐号】也能够调试,那么还需要如下设置:
  1、在【服务器】上运行dcomcnfg.exe;
  2、在【默认安全机制】中【默认访问权限】右边点击【编辑默认值】选择允许调试的帐号类型,如users用户类型,sample帐号有包含users组;
  3、重新启动sqlserver服务;
  3、在客户端上创建与服务帐号密码一样的用户,如sample;
  做到这步就可以通过查询分析器的调试功能进行单步调试了。
  注:第二步更改“启动服务帐户”,在第一次登录之前,必须更改用户密码。
  不然,event log:
  以当前密码登录的尝试因下列错误而宣告失败:
  在第一次登录之前,必须更改用户密码。

原创粉丝点击