Recent comments

None


İçerik Ara











Yasal Uyarı
Bu sitede sunulan tüm bilgi ve dökümanlar Turgay Sahtiyan tarafından yazılmaktadır. Yazıların kaynak göstermek şartıyla kullanılması serbesttir.

© Copyright 2009-2013
Takvim
<<  Ağustos 2017  >>
PaSaÇaPeCuCuPa
31123456
78910111213
14151617181920
21222324252627
28293031123
45678910
Keywords

Bildiğiniz gibi her SQL komutu çalıştırıldığında ilgili komut için bir Query Plan oluşturulur ve Plan Cache’de saklanır. Eğer ilgili sorgu parameterize edilebilen bir sorgu ise (Stored Procedure gibi) ve bu sorgu için daha önce bir Query Plan oluşturulduysa, tekrar bir Query Plan oluşturulmaz ve daha önce Plan Cache’de saklanan Query Plan kullanılır.

Query Plan oluşturulma işlemi masraflı bir işlemdir. Bu yüzden oluşturulmuş Query Plan’larının saklanıp kullanılarak tekrar tekrar Query Plan oluşturulmaması performans açısından önemli bir durumdur.

İşte bu bakış açısıyla exec ve sp_executesql komutları ile Dynamic SQL kullanımında ortaya çıkan farklılıkları bu makalede inceleyip, sp_ExecuteSQL’in exec yerine neden tercih edilmesi gerektiğini örnekler ile görüyor olacağız.

[more]

Her iş gereksinimi sabit SQL komutları ile Stored Procedure’lere gömülemeyebilir. Örneğin parametre olarak verilen tablo adına çekilecek sorguda Dynamic SQL kullanma zorunluluğumuz ortaya çıkabilir.

Ya da başka bir örnek verecek olursak; sorguda bulunan where bloğunda kullanılacak ifadeler duruma göre farklılık gösterebilir ya da parametre olarak gönderilebilir.

İşte böyle bir durumda sabit bir SQL ifadesi yerine Dynamic SQL kullanmamız gerekebilir. Peki ama komut olarak Exec mi yoksa sp_executeSQL komutunu mu kullanmalıyız?

Bu soruya bir örnek ile cevap bulmaya çalışalım.

Örneğimizde kullandığımız where bloğu değişkenden alındığı için Dynamic SQL kullanmak zorunda kalıyoruz. İlk blokta exec komutu ile kullanıp Cache’lenen Plan’larının tekrar kullanılmadığını göreceğiz.

İkinci örnekte ise aynı işlemi sp_executesql ile yapacağız ve cache’lenen planların tekrar kullanıldığını göreceğiz.

Exec ile Dynamic SQL Kullanımı

--İşlemlere başlamadan önce Plan Cache'i temizliyoruz.
--DİKKAT:Bu işlemi production ortamında YAPMAYINIZ!!
dbcc freeproccache

--exec komutu ile Dynamic SQL kullanıyoruz.
declare @str varchar(max)='',
		@param1 varchar(50)='',
		@param2 varchar(50)=''
declare @strin varchar(50)
set @param1='1'
set @param2='2'
set @str='select * from AdventureWorks.Person.Address where AddressID in ('+@param1+','+@param2+')'
exec(@str)

--Cache'lenen query Planlara bakalım.
select st.text,* 
from sys.dm_exec_cached_plans cp
cross apply sys.dm_exec_sql_text(cp.plan_handle) st
where (st.text like '%select * from AdventureWorks%')
and st.text not like '%select st.text%'

--exec komutu ile aynı sorguyu tekrar çekiyoruz
declare @str varchar(max)='',
		@param1 varchar(50)='',
		@param2 varchar(50)=''
declare @strin varchar(50)
set @param1='3'
set @param2='4'
set @str='select * from AdventureWorks.Person.Address where AddressID in ('+@param1+','+@param2+')'
exec(@str)

--Cache'lenen query Planlara bakalım.
select st.text,* 
from sys.dm_exec_cached_plans cp
cross apply sys.dm_exec_sql_text(cp.plan_handle) st
where (st.text like '%select * from AdventureWorks%')
and st.text not like '%select st.text%'

--Her 2 sorgu için 2 ayrı plan oluşturulduğunu görüyoruz.

 

1

sp_executeSQL ile Dynamic SQL Kullanımı

Şimdi aynı işlemi sp_executeSQL ile tekrarlayalım.

--İşlemlere başlamadan önce Plan Cache'i temizliyoruz.
--DİKKAT:Bu işlemi production ortamında YAPMAYINIZ!!
dbcc freeproccache

--sp_executesql ile aynı işlemi yapalım
--sp_executesql 1
declare @param1 int,
	    @param2 int
set @param1=1
set @param2=2
exec sp_executesql N'select * from AdventureWorks.Person.Address where AddressID in (@1,@2)'
		,N'@1 int, @2 int'
		,@param1, @param2
		
--Cache'lenen query Planlara bakalım.
select st.text,* 
from sys.dm_exec_cached_plans cp
cross apply sys.dm_exec_sql_text(cp.plan_handle) st
where (st.text like '%select * from AdventureWorks%')
and st.text not like '%select st.text%'		

--sp_executesql 2
declare @param1 int,
	    @param2 int
set @param1=3
set @param2=4
exec sp_executesql N'select * from AdventureWorks.Person.Address where AddressID in (@1,@2)'
		,N'@1 int, @2 int'
		,@param1, @param2
		
--Cache'lenen query Planlara bakalım.
select st.text,* 
from sys.dm_exec_cached_plans cp
cross apply sys.dm_exec_sql_text(cp.plan_handle) st
where (st.text like '%select * from AdventureWorks%')
and st.text not like '%select st.text%'		

--Aynı Query Plan'ın 2 defa kullanıldığını
--tekrar yeni bir Query Plan oluşturulmadığını görüyoruz

 

2

Gördüğünüz gibi her 2 sorgu için ayrı ayrı Query Plan oluşturulmadı ve aynı Query Plan kullanıldı.

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Tablolar üzerinde çalışacağım hemen hemen her makalemde bir örnek çalışma tablosu oluşturuyorum. Bu makalemde ise bu çalışma tablolarını daha hızlı bir şekilde nasıl oluşturabileceğimizi görüyor olacağız.

[more]

Normalde şu ana kadar çalışma tablomu hep şu tarz bir script ile dolduruyordum.

Create Table DenemeTbl1 (Col1 int, Col2 char(15), Col3 char(25), Col4 char(20))
GO
SET NOCOUNT ON
GO
declare @i int=1
while @i<=100000 begin
  insert DenemeTbl1
    select @i
	  ,'turgay'+cast(@i as varchar(6))
	  ,'sahtiyan'+cast(@i as varchar(6))
	  ,'SQLServerDBA'+cast(@i as varchar(6))
  set @i=@i+1
end

 

100.000 kayıt insert eden yukarıdaki işlem benim laptop’ımda 13 saniye sürmekte.

Şimdi aynı işlemi değişik bir teknik ile yapacağım.

Create Table DenemeTbl2 (Col1 int, Col2 char(15), Col3 char(25), Col4 char(20))
GO
INSERT DenemeTbl2 WITH (TABLOCKX)
	SELECT rn
		  ,'turgay'+cast(rn as varchar(6))
		  ,'sahtiyan'+cast(rn as varchar(6))
		  ,'SQLServerDBA'+cast(rn as varchar(6))
	FROM
	(SELECT TOP (100000) rn = ROW_NUMBER() OVER (ORDER BY c1.number)
	FROM master..spt_values AS c1 CROSS JOIN master..spt_values AS c2
	ORDER BY rn)
	AS x;

 

Bu işlem ise sadece 167 milisaniye sürmekte.

Sürekli performanstan bahseden biri olarak kendi işlemlerimizi performansız yapmak bize yakışmaz :) Bu yüzden bundan sonraki makalelerimde bu tekniği kullanacağım.

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Query plan oluşturmak CPU ve IO açısından pahalı bir işlemdir. Bu yüzden oluşturulmuş olan Query Plan’lar Plan Cache’de saklanır ve aynı sorgu tekrar geldiğinde kullanılır. Objelerin (tablo vs.) two-part-name şeklinde kullanılmamaları bazı durumlarda Plan’ın tekrar kullanılmamasına ve yeni plan oluşturulmasına sebep olur. Bu da hem memory’nin optimum kullanılmamasına hem de performans sıkıntısına sebebiyet verir.

[more]

İlk olarak two-part-name ve one-part-name ifadelerinin ne olduğunu anlatmaya çalışayım. Aşağıdaki örnekte de gördüğünüz üzere schema adı kullanılarak obje çağırılmasına two-part-name kullanımı, schema adı kullanılmadan çağırılmasına ise one-part-name kullanımı denilmektedir.

--one-part-name kullanimi
select * from tbl1
--one-part-name kullanimi
select * from dbo.tbl1

 

One-Part-Name kullanılarak çağırılan objelerde bazı durumlarda Plan Cache’de bulunan plan kullanılmayıp yeni plan oluşturulur. Bunun sebeplerinden biri sorguyu çalıştıran user’ların farklı default schema’lara sahip olmalarıdır. Şimdi gelin bu durumu gerçekleştirmeye çalışalım.

--Bir calisma DB'si olusturuyoruz
create database dbTwoPart
go
use dbTwoPart
go
--Bir calisma tablosu olusturuyoruz
create table tblTwoPart(a int identity(1,1), b varchar(10))
GO
--tabloya 100 kayit insert ediyoruz
insert tblTwoPart
  select 'b'
GO 100  
--2 adet user olusturuyoruz
create user user1 without login
create user user2 without login
GO
--Bu user'lara yukaridaki tablo icin select hakki veriyoruz
grant select on tblTwoPart to user1,user2
GO
--Ilk user'in default schema'sini dbo olarak birakirken
--ikinci user'in default schema'sini farkli bir schema yapiyoruz.
create schema User2Schema authorization user2
go
alter user user2 with default_schema=User2Schema
GO

 

Ön tanımlamalar tamam. Şimdi sorguları çalıştırıp örneği gerçekleştirmeye başlayabiliriz.

İlk olarak One-Part-Name kullaniminda neler olduğunu görelim.

--Islemlere baslamadan once Plan Cache'i temizliyoruz
DBCC FREEPROCCACHE
GO
--Tabloya user1 olarak select cekiyoruz.
--Tabloya erisim metodu olarak One-Part-Name kullaniyoruz
execute as user='user1'
go
select * from tblTwoPart
go
revert
go
--Ayni islemleri simdi de user2 ile yapiyoruz
execute as user='user2'
go
select * from tblTwoPart
go
revert
go
--Query Plan'lari kontrol ediyoruz
select st.text, qs.execution_count, qs.*, p.*
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text(qs.sql_handle) st
cross apply sys.dm_exec_query_plan(plan_handle) p
where st.text not like '%st.text%'
order by qs.execution_count desc
GO

 

1

Gördüğünüz gibi 2 sorgu için ayrı ayrı plan oluşturuldu. Bunun sebebi plan_attribute’unda bulunan user_id bilgisi.

select * from sys.dm_exec_plan_attributes(0x06001F001992EB164001AC88000000000000000000000000)  
select * from sys.dm_exec_plan_attributes(0x06001F001992EB164041D284000000000000000000000000)

 

2

User_id kısmında sorguyu çalıştıran user’ın default schema bilgisi yazmakta. Yukarıda tanımladığımız 2 user’ın default schema’ları birbirinden farklı olduğu için aynı plan’ı kullanamadılar.

Peki Two-Part-Name kullansaydık durum nasıl olacaktı?

--Islemlere baslamadan once Plan Cache'i temizliyoruz
DBCC FREEPROCCACHE
GO
--Tabloya user1 olarak select cekiyoruz.
--Tabloya erisim metodu olarak One-Part-Name kullaniyoruz
execute as user='user1'
go
select * from dbo.tblTwoPart
go
revert
go
--Ayni islemleri simdi de user2 ile yapiyoruz
execute as user='user2'
go
select * from dbo.tblTwoPart
go
revert
go
--Query Plan'lari kontrol ediyoruz
select st.text, qs.execution_count, qs.*, p.*
from sys.dm_exec_query_stats qs
cross apply sys.dm_exec_sql_text(qs.sql_handle) st
cross apply sys.dm_exec_query_plan(plan_handle) p
where st.text not like '%st.text%'
order by qs.execution_count desc
GO

 

3

Two-Part-Name kullandığımız için aynı plan tekrar kullanılabildi. Plan_Attributes’a bakarsak;

4

User_id kısmında -2 yazmakta. Bunun anlamı; Two-Part-Name kullanıldığı için user_id’nin artık bir önemi kalmamış ve bu yüzden user_id’ye bunu belirtmek için -2 yazılmış durumdadır.

Bugün üzerinde durduğumuz örnek, ufak bir kodlama farkının performansa olan etkisine çok güzel bir örnek. Ayrıca Ona-Part-Name kullanımının Two-Part-Name kullanımına göre güvenlik açıkları da vardır. İlerleyen günlerde bu konuya da değinmeye çalışacağım.

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bugünkü makalemde bir sonraki makalemde kullanacağım bir ifadeden, Safe Query mantığından bahsediyor olacağım. Hangi durumlarda Query’nin Safe olarak düşünüldüğü hangi durumlarda Safe olmadığını görüyor olacağız

[more]

En basit anlatımıyla; bir Query’nin kullanılan parametrelere göre oluşturulabilecek birden fazla “en iyi plan” ı var ise bu Query Safe değildir. Ya da tersinden söyleyecek olursak; bir Query hangi parametreleri kullanırsa kullansın hep aynı plan onun için en iyi plan ise bu Query Safe’dir.

Bir örnek ile ne demek istediğimi anlatmaya çalışayım. Örneğin bir tablonuz var ve bu tabloya Col1 kolonu üzerinden select çekiyorsunuz. Diyelim ki Col1 kolonu için “a” değerini kullandığınızda index seek yapılıyor ama “b” değerini kullandığınızda index scan yapılmasına karar veriliyor. İşte bu şekilde kullanılan parametreye göre en iyi Query Plan değişiyorsa bu Query Safe değildir.

Canlı bir örnek yaparak konuyu daha da pekiştirelim.

Safe Query Örneği

Aşağıdaki gibi bir tablo create edip içerisine veri basıyorum.

create Table SafeQuery(ID int identity(1,1),col1 varchar(5),col2 varchar(10))
GO
declare @i int=65
while @i<=10000 begin
  insert SafeQuery select CAST(@i as varchar(5)),'Turgay'
  set @i=@i+1
end
GO
Create clustered index IX_1 on SafeQuery(ID)
GO
Create nonclustered index IX_2 on SafeQuery(col1)
GO

 

Sorguyu incelediğinizde sizin de farkedebileceğiniz gibi col1 kolonuna hep farklı değerler bastım ve en nihayetinde tablomda her col1 değeri için 1 kayıt oluşturdum.

Dolayısıyla bu tablo üzerinden yapacağım her sorgu aynı Query Plan’ı en iyi plan olarak kullanacaktır. O yüzden bu Query Safe’dir.

1

UnSafe Query Örneği

Aşağıdaki gibi bir tablo create edip içerisine veri basıyorum.

create Table UnSafeQuery(ID int identity(1,1),col1 varchar(1),col2 varchar(10))
GO
insert UnSafeQuery select 'a','Turgay'
GO 1
insert UnSafeQuery select 'b','Turgay'
GO 10
insert UnSafeQuery select 'c','Turgay'
GO 100
insert UnSafeQuery select 'd','Turgay'
GO 1000
Create clustered index IX_1 on UnSafeQuery(ID)
GO
Create nonclustered index IX_2 on UnSafeQuery(col1)
GO

 

Oluşan tabloda her Col1 değerine karşılık farklı sayıda kayıt dönecektir. Dolayısıyla bu tablo üzerinden Col1 kolonu kullanılarak çekilen sorgular farklı plan’ları en iyi plan olarak görebilirler.

Örneğin aşağıdaki sorgulardan ilki 1 kayıt döndürdüğü için Index Seek + LookUp yaparken, ikinci sorgu 1000 kayıt döndürdüğü için Clustered Index Scan yapmaktadır.

2

Bu yüzden yani kullanılan parametreye göre Query Plan’lar değiştiği için bu Query Safe değildir.

 

Bu makalem bir sonraki “Simple-Forced Parameterization” makalemin alt yapılarından birisi olacak. O yüzden daha öncesinde bu makaleyi kaleme almak istedim.

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Where Bloğunda Case Kullanmayın makalemden sonra ikinci “kullanmayın” makalem ile karşınızdayım :) Bu makalemde where bloğunda kullanılan “Collate” anahtar kelimesinin performansa olan etkisini örnekler ile inceleyeceğiz.

[more]

Aslında problem sadece “Collate” anahtar kelimesi değil. Index’in tanımlı olduğu kolondaki veriyi manupüle eden her anahtar kelimede aynı sıkıntı mevcut. Bunun da nedeni, en basitinden, Index’in tanımlı olduğu kolonun manupule edildiğinde sıralamanın(order) değişme ihtimali. Böyle bir durumda yani artık sıralı olmayan bir Index Key’de seek işleminin yani arama işleminin sağlıklı olarak yapılamamasından kaynaklı olarak Scan yapılma zorunluluğudur. Tekrar ayrı bir paragrafta belirterek altını çizmek gerekirse;

Collate Index Scan yapılmasına neden olur

Şimdi “Collate” özelinde bir örnek yapalım. Bu örneğimde aynı sorguyu “Collate” anahtar kelimesi varken ve yokken çalıştırıp Query Plan’larını karşılaştıracağım.

İlk olarak çalışma tablomuzu oluşturalım.

--Calisma DB'si olustuyorum.
--Collation olarak Latin1_General_CI_AS kullanıyorum.
Create Database DBCollateDeneme COLLATE Latin1_General_CI_AS
GO
Use DBCollateDeneme
GO
--Calisma tablosu olusturuyorum
create table tblDeneme1(a varchar(37), b char(400))
GO
--Tabloya 10000 kayıt basiyorum
declare @i int=0
declare @a char(37)
while @i<10000 begin
  select @a=CAST(NEWID() as CHAR(37))
  insert tblDeneme1
    select @a,REPLICATE('b',400)    
  set @i=@i+1
end
--a kolonu uzerine Clustered Index tanimliyorum
Create Clustered Index CX_1 on tblDeneme1(a)

 

Şimdi 2 farklı sorguyu aynı ekranda çekip Query Planlarını karşılaştıralım.

--Sorgu1
select * from tblDeneme1 
where a ='19D7D1DA-867A-4903-9B22-88171CF9D451' 
--Sorgu2
select * from tblDeneme1 
where a ='19D7D1DA-867A-4903-9B22-88171CF9D451' COLLATE TURKISH_CI_AS

 

image

Resimde de gördüğünüz üzere Collate kullanılmazken Index Seek yapılarak getirilen sorgu Collate kullanılınca Index Scan yaparak getirilmiş durumda.

Tekrar etmek gerekirse, Collate Index Scan yapılmasına neden olmaktadır. Bu yüzden gerçekten gerekli olan yerlerde Collate kullanmaya özen göstermek gerekmektedir.

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Keskin bir ifade oldu farkındayım :) O yüzden cümlenin başına “çok zorda kalmadıkça diye” ekleyelim. Kullanmayın deme sebebim ise where bloğunda case kullanımı, istatistikler üzerinden estimated rows hesabı yapılırken yanlış hesaplamalar yapılmasına dolayısıyla da estimate edilecek kayıt sayısının doğru tahminlenememesi anlamına gelmektedir. Bu durumda da Query Plan sağlıklı bir şekilde oluşturulamayacak ve gereksiz IO yapılmasından dolayı performans sıkıntısı ortaya çıkacaktır. Bugünkü yazımda bahsettiğim bu konuyu örnekler ile görüyor olacağız.

[more]

Örneklerimde AdventureWork.Person.Address tablosunu kullanıp City kolonu üzerinden sorgular çekeceğim. Bu yüzden ilk olarak City kolonu üzerine bir NonClustered Index oluşturuyorum.

IF  not exists (select * from sys.indexes where object_id = OBJECT_ID('Person.Address') AND name = 'IX_1')
  create nonclustered index IX_City on Person.Address
	(City)

 

Person.Address tablosuna çekeceğim sorgularda City bilgisi olarak Bothell. Normal şartlarda Bothell için çekilen sorguların Query Planı şu şekilde olmakta.

1

Şimdi where bloğunda case anahtar kelimesi içeren şöyle bir sorgu yazıyorum.

declare @val1 varchar(10)=NULL
declare @val2 varchar(10)='Bothell'
select * from Person.Address 
	where City = (case when @val1 is null then @val2
					else @val1 end)

 

Sorguda da gördüğünüz gibi eğer @val1 değişkeni null değil ise city bilgisi olarak bu değişken, eğer null ise city bilgisi olarak @val2 değişkeni kullanılacak. Bu durumda yukarıdaki sorguyu çalıştırdığımda “Bothell” için çalıştırmış olacağım.

2

Bothell için normal şartlarda Index Seek yaptığını daha önce görmüştük. Oysaki bu sorguda case kullandığım için artık Clustered Index Scan yapılmakta.

Bunun nedeni ise Estimated Rows kısmında gördüğümüz istatistikler üzerinden hesaplanan “tahmini dönecek kayıt sayısı” bilgisinin normalden çok olmasıdır. Aşağıdaki resimde 2 sorgu için tahminlenen kayıt sayılarını görebilirsiniz.

3

Bu yüzden ilk sorgu için Index Seek yapılmasına karar verilirken, ikinci sorguda dönecek kayıt sayısı olması gerekenden fazla yani yanlış hesaplandığı için Clustered Index Scan yapılmasına karar veriliyor.

İşte bu sebepten dolayı sorguların where bloğunda case kullanımından olabildiğince kaçınmakta fayda olduğunu söyleyebilirim.

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Concat_Null_Yields_Null özelliği ile string birleştirmelerinde NULL ifadenin boş string olarak algılanması dolayısıyla da sonucun NULL gelmemesi için kullanılır.

[more]

Defualt olarak Concat_Null_Yields_Null özelliği ON durumdadır. Bu durumda string bir ifade ile NULL bir ifade birleştirildiğinde sonuç NULL olarak dönecektir. Örneğin Select ‘abc’+NULL ifadesinin sonucu NULL olacaktır.

Eğer Concat_Null_Yields_Null özelliği OFF yapılırsa yukarıdaki aynı örnek ‘abc’ sonucunu döndürecektir.

Şimdi bu 2 örneği SSMS’te gerçekleştirelim.

--CONCAT_NULL_YIELDS_NULL özelliği ON iken
--sonuç NULL olacaktır
SET CONCAT_NULL_YIELDS_NULL ON;
GO
SELECT 'abc' + NULL ;
GO

--CONCAT_NULL_YIELDS_NULL özelliği OFF iken
--sonuç 'abc' olacaktır
SET CONCAT_NULL_YIELDS_NULL OFF;
GO
SELECT 'abc' + NULL; 
GO

 

Son olarak önemli bir not düşerek yazıyı noktalayalım. SQL Server’ın ileriki versiyonlarında Concat_Null_Yields_Null özelliğinin sürekli ON yapılması gündemde. Ve yukarıdaki örneklerde olduğu gibi dışarıdan SET edildiği takdirde hata alıyor olacaktır. Bu yüzden geliştirme ortamlarında artık kullanılması tavsiye edilmemektedir.

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bildiğiniz gibi NewID() fonksiyonu 16 byte büyüklüğünde rasgele unique değer oluşturmak için kullanılır. NewID’nin customize edilmiş hali olan NewSequentialID() fonksiyonu ise her defasında daha önce oluşturulan GUID’lerden daha büyük bir GUID oluşturmak için kullanılmakta.

[more]

SQL Server 2008 sürümüyle gelen NewSequentialID() fonksiyonu her çalıştırıldığında son windows restart’ından sonra oluşturulmuş GUID’lerden daha büyük bir GUID oluşturmayı taahhüt eder. Bir sonraki restart’dan sonra ise daha küçük bir GUID’den başlanma ihtimali vardır. Ama her halukarda NewID() gibi global olarak unique bir değer oluşturur.

Kullanılma yeri olarak Clustered Index key örneğini verebiliriz. Bazı geliştirici ya da DBA arkadaşlar clustered key olarak GUID kullanırlar. Eğer oluşturulan GUID rasgele olarak yani sıralı olarak oluşturulmaz ise tabloda Page Split ve Fragmantation oluşur.

NewSequentialID() sıralı GUID oluşturma özelliği ile bu sıkıntıları ortadan kaldırır. Tabi unutmamak gerekir ki, Windows restartından sonra bu sıranın kaybolma ve baştan başlanma ihtimali vardır.

NewSequentialID() sadece Default constraint olarak kullanılabilir.

CREATE TABLE myTable (Col1 uniqueidentifier DEFAULT NEWSEQUENTIALID()) 

 

NewId() gibi select çekilmek istendiğinde ise şu şekilde bir hata alınacaktır.

image

Son olarak şunu belirtmek istiyorum. NewSequentialID() sıralı olmasından dolayı bir sonraki create edilecek ID tahmin edilebilir. Bu yüzden güvenlik kaygısı olunan durumlarda kullanılmaması gerekir.

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


SQL Server 2011 Denali ile beraber gelen CONCAT fonksiyonu string ifadeleri birleştirmek için kullanılabilir.

[more]

CONCAT fonksiyonunun syntax’ı şu şekildedir;

CONCAT ( string_value1, string_value2 [, string_valueN ] )

Minimum 2 string değerin parametre olarak verilmesi gerekir. Aksi halde fonksiyon hata alacaktır.

Kullanımı şu şekildedir;

SELECT CONCAT ('Turgay ','Sahtiyan ', 'SQL ', 'Server ', 'DBA') AS Result

 

Result
===========
Turgay Sahtiyan SQL Server DBA

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bugünkü yazımda SQL Server 2011 Denali ile beraber gelen 2 yeni mantıksal operatörden bahsedeceğim.

[more]

CHOOSE

Belirli bir listeden index parametresi ile verilmiş olan değeri getirir. Syntax’ı şu şekildedir;

CHOOSE ( index, val_1, val_2 [, val_n ] )

Kullanım örneklerine bakarsak;

SELECT CHOOSE ( 1, 'Turgay', 'Sahtiyan', 'SQL', 'Server', 'DBA' ) AS Result;

 

Result
=============
Turgay

SELECT CHOOSE ( 5, 'Turgay', 'Sahtiyan', 'SQL', 'Server', 'DBA' ) AS Result;

 

Result
=============
DBA

 

IIF

Parametre olarak verilen ifadenin doğru olduğu durumda 1. Değeri yanlış olduğu durumda 2. Değeri döndürür. Syntax’ı şu şekildedir;

IIF ( boolean_expression, true_value, false_value )

Kullanım örneklerine bakacak olursak;

SELECT IIF(1=1,'EŞİT','EŞİT DEĞİL') AS Result

Result
=============
EŞİT

 

SELECT IIF(4>5,'4, 5''ten büyüktür','4, 5''ten büyük değildir') AS Result

Result
=============
4, 5'ten büyük değildir

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


SQL Server 2011 Denali ile beraber 7 tane yeni Datetime fonksiyonu geldi. Bugün bu fonksiyonlardan en çok hoşuma giden EOMONTH’u örnekler ile inceleyip, diğer kalan 6 tane için de ufak bilgiler vereceğim.

[more]

EOMONTH fonksiyonu parametre olarak verilen tarihin içinde bulunduğu ayın en son gününü verir.

Örneğin aşağıdaki kod ile 2011 Şubat ayının son gününün hangi gün olduğunu öğrenebiliriz.

set dateformat dmy

DECLARE @date DATETIME;
SET @date = '01.02.2011';
SELECT EOMONTH ( @date ) AS Result;

Result
===============
2011-02-28 00:00:00.000

Ya da şu şekilde 2011 Şubat’tan sonra gelen 4.ayın son gününü öğrenebiliriz.

set dateformat dmy
DECLARE @date DATETIME;
SET @date = '01.02.2011';
SELECT EOMONTH (@date,4) AS Result;

Result
===============
2011-06-30 00:00:00.000

EOMONTH fonksiyonu hariç SQL Server 2011 Denali ile gelen diğer DateTime fonksiyonları ve kısa açıklamaları şu şekilde.

  • DATEFROMPARTS : Parametre olarak verilen yıl,ay,gün değerlerinden tarih değeri oluşturmak için kullanılır.
  • DATETIME2FROMPARTS : Parametre olarak verilen yıl, ay, gün, saat, dakika, saniye, hassaslık değerlerinden datetime2 formatında tarih-saat değeri oluşturmak için kullanılır.
  • DATETIMEFROMPARTS : Parametre olarak verilen yıl,ay,gün,saat,dakika,saniye değerlerinden datetime formatında tarih-saat değeri oluşturmak için kullanılır.
  • DATETIMEOFFSETFROMPARTS : Parametre olarak verilen yıl, ay, gün, saat, dakika, saniye ve offset değerlerinden datetimeoffset formatında tarih-saat değeri oluşturmak için kullanılır.
  • SMALLDATETIMEFROMPARTS : Parametre olarak verilen yıl, ay, gün, saat, dakika değerlerinden smalldatetime formatında tarih-saat değeri oluşturmak için kullanılır.
  • TIMEFROMPARTS : Parametre olarak verilen saat, dakika, saniye değerlerinden time formatında saat değeri oluşturmak için kullanılır.

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bildiğiniz gibi LinkedServer tanımlayarak A server’ı üzerinden B Server’ında select,update gibi işlemleri yapabilmemiz mümkün.

Bugün anlatacağım OpenDataSource komutu ile linked server’a gerek kalmadan bu işlemleri nasıl yapabileceğimizi görüyor olacağız.

[more]

OpenDataSource kullanılabilmesi için sorguyu çalıştıran server’da Ad Hoc Distributed Queries özelliğinin enable edilmesi gerekmektedir.

Aksi halde şöyle bir hata alınır.

Msg 15281, Level 16, State 1, Line 1
SQL Server blocked access to STATEMENT 'OpenRowset/OpenDatasource' of component 'Ad Hoc Distributed Queries' because this component is turned off as part of the security configuration for this server. A system administrator can enable the use of 'Ad Hoc Distributed Queries' by using sp_configure. For more information about enabling 'Ad Hoc Distributed Queries', see "Surface Area Configuration" in SQL Server Books Online.

Ad Hoc Distributed Queries özelliğini aktif etmek için sorguyu çalıştıran server’da şu script’i çalıştırabilirsiniz.

exec sp_configure 'show advanced options',1
ReConfigure with override
exec sp_configure 'Ad Hoc Distributed Queries',1
ReConfigure with override
exec sp_configure 'show advanced options',0
ReConfigure with override

Şimdi kullanıma bakalım.

Örneğin B server’ında bulunan bir tabloya select çekmek istiyorsunuz. Bunu OpenDataSource ile yapabilmek için önce B server’ında bulunan bu tabloya select çekme yetkisine sahip bir kullanıcı tanımlıyoruz. Daha sonrada bu kullanıcıyı kullanarak A server’ında şöyle bir view create ederek B server’ında bulunan bu tablonun A server’ından select çekilebilmesini sağlıyoruz.

A server’ında create edilecek view;

create view myView
with encryption
as
SELECT *
FROM OPENDATASOURCE('SQLNCLI','Data Source=ServerB;User ID=LoginName;Password=password'
		    ).AdventureWorks.Person.Address

 

Gördüğünüz gibi view create’in de with encryption keyword’unu kullandık. Bunun sebebi de tanımlamış olduğumuz kullanıcının kullanıcı adı ve şifre bilgilerini göstermek istemememiz.

Şimdi update işlemini nasıl yapabiliriz diye bakalım.

Bunun için gene encrypted olarak bir Stored Procedure (SP) create edeceğiz.

create proc sp_UpdPersonAddress
	@AddressID int,
	@PostalCode nvarchar(15)
with encryption
as
UPDATE OPENDATASOURCE('SQLNCLI','Data Source=ServerB;User ID=LoginName;Password=password'
		   ).AdventureWorks.Person.Address
SET PostalCode = @PostalCode
where AddressID=@AddressID

 

Bu update SP’si vasıtasıyla B Server’ında bulunan Person.Address tablosunun PostaCode kolonunu A server’ı üzerinden update edebiliriz.

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bu aralar sıkça kullandığım ve işimi oldukça kolaylaştıran ufak bir syntax’tan bahsetmek istiyorum.

GO komutu ile belirli bir kod bloğunu n kere çalıştırmak.

[more]

Fazla uzatmadan syntax’ı görelim.

insert tblGoDeneme 
  select 1,'a' 
GO 100

 

Bu script, GO’nun yanına yazılan 100 rakamı sayesinde tabloya 100 kayıt insert edecektir.

Aynı işlemi while döngüsü kurarakta yapmak mümkün. Ama sizin de görebileceğiniz gibi GO ile yapmak çok daha temiz ve kısa bir script sağlamakta.

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


SQL Server login’lerine eklemiş olduğunuz bir Active Directory kullanıcı grubu var. Peki bu kullanıcı grubu hangi kullanıcıları kapsamakta.

Ya da soruyu daha detaylandırırsak; Active Directory’de yada local makinada olan bir kullanıcı grubunun üyelerine T-SQL ile bakabilir miyiz?

Cevabımız evet.

[more]

Aşağıdaki kod ile bir Active Directory grubuna dahil olan kullanıcıları sorgulamamız mümkün.

exec xp_logininfo 'DomainName\GroupName','members'

 

Bu şekilde Domain Admin arkadaşlara hiç gitmeden kendi işimizi gene kendimiz görmüş oluyoruz Smile

Script i kullanırken dikkat etmemiz tek nokta, sorgulamak istediğimiz grubun SQL Server Login’ler içerisinde tanımlanmış olma zorunluluğu. Eğer tanımlı değilse sorgu sonucu boş dönecektir.

 

İyi çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


ObjectProperty ile ilgili database’deki bir objenin bazı özelliklerini sorgulayabiliriz. Örneğin hangi tablolarımda Cluster Index var gibi bir soruya sys.indexes’i join’lemeden objectproperty’i kullanarak ulaşmak mümkün.

Bugünkü yazımda objectproperty kullanarak 4 adet faydalı script yazacağız.

[more]

1. Clustered ya da NonClustered Index İçeren Tablolar

select * from sys.tables
where objectproperty(object_id,'TableHasIndex')=1

2. Clustered Index İçeren Tablolar

select * from sys.tables
where objectproperty(object_id,'TableHasClustIndex')=1

3. NonClustered Index İçeren Tablolar

select * from sys.tables
where objectproperty(object_id,'TableHasNonclustIndex')=1

4. Identity Kolon İçeren Tablolar

select * from sys.tables
where objectproperty(object_id,'TableHasIdentity')=1

Eşitliklerde 1 yerine 0 kullanarak tam tersi sonucu almak mümkün. Örn. Clustered Index İçermeyen Tablolar gibi.

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bildiğiniz gibi yazdığımız SP,View,Function gibi object’lerde Index seçimini Query Optimizer’a bırakmayıp kendimiz Force ederek istediğimiz bir Index’i kullandırtabiliriz. Fakat Best practice olarak bu yöntem önerilmez. Çünkü Force edilen Index’i kaldırma gibi bir durum oluşursa bu index’in force edildiği script’ler hata verecektir. Ayrıca ilk force edildiği anda belki ilgili index ilgili script’e uyuşmaktaydı, ama daha sonra bu index’te yapılacak bir değişiklik artık query’e tam olarak uyuşmayacak ve performans sıkıntısı doğuracaktır.

Bugün sizin ile paylaşacağım script ile hangi object’lerde Index Force’un kullanıldığını sorgulayacağız.

[more]

İlk olarak Index Force’un Syntax’ına 1-2 örnek vererek bakalım.

--Parantez ile kullanabiliriz
Select * 
from Sales.SalesOrderHeader WITH (INDEX (IX_SalesOrderHeader_CustomerID))
where SalesOrderID=43659

--Eşittir ile kullanabiliriz
Select * 
from Sales.SalesOrderHeader WITH (INDEX=IX_SalesOrderHeader_CustomerID)
where SalesOrderID=43659

--Index'in ID'si ile kullanabiliriz.
Select * 
from Sales.SalesOrderHeader WITH (INDEX=5)
where SalesOrderID=43659

 

Şimdi Index Force kullanılmış Script’leri (SP,View,Function vs.) sorgulayacak script’e bakalım.

create table #tmp1 (DBName sysname,object_id int, name sysname, type varchar(3), type_desc varchar(50), OriginalText varchar(max), text varchar(max))

exec sp_msforeachdb 'use ?;
insert #tmp1
select ''?'' as DBName,sm.object_id,o.name,o.type,o.type_desc
      ,definition as OriginalText 
      ,replace(replace(replace(replace(replace(replace(replace(definition,'' index'',''(INDEX''),'' INDEX'',''(INDEX''),'' '',''''),''CHARINDEX'',''''),''PATINDEX'',''''),''charindex'',''''),''patindex'','''') as text
from sys.sql_modules sm
left join sys.objects o on o.object_id=sm.object_id
where o.type in (''AF'',''FN'',''P'',''IF'',''FS'',''FT'',''PC'',''RF'',''TA'',''TF'',''TR'',''V'',''X'') '
 
select * 
from #tmp1 t
where t.text like '%(Index(%' or t.text like '%(Index=%' 
order by t.name
 
drop table #tmp1

 

Sorgu sonucunda içinde Index Force kullanılmış object’lere erişmiş olacağız.

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bugün bir developer arkadaşımın benden istediği script’i sizinle paylaşmak istiyorum.

[more]

İstek şu şekilde; ABC12345 gibi bir string’de bulunan A,B,C gibi AlfaNumeric karakterleri silip sadece 12345’i bırakma.

Bunun için yazmış olduğum function aşağıdaki gibi;

create function dbo.ExcludeChars (@str varchar(max))
RETURNs varchar(max)
AS
BEGIN

declare @ix int=len(@str)
declare @i int=1

declare @newString varchar(max)=''

while @i<=@ix begin
  if ASCII(SUBSTRING(@str,@i,1))>=48 and ASCII(SUBSTRING(@str,@i,1))<=57 
    select @newString=@newString+SUBSTRING(@str,@i,1)
  set @i=@i+1
end

return @newString

END

 

Kullanımı da şu şekilde;

select dbo.ExcludeChars('ABC12345')

 

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


SQL Server 2011 “Denali” ile gelen bir diğer özellik de Sequence Objects yani Sıralama objesi. Table’larda bulunan Identity kolonuna çok benzeyen Sequence ile otomatik artan sayılar oluşturmak mümkün. Identity’den en büyük farkı ise bir tablo ile sınırlı kalmaması, istenirse 5 tablo için aynı sequence object’in kullanılabilmesi.

[more]

Bir örnek vererek neden böyle bir object’e ihtiyaç duyduğumuzu açıklamaya çalışalım. Örneğin bir ticari program yapıyorsunuz ve bu ticari programda internet üzerinden ve perakende olarak satış yapma opsiyonları var. Ve siz bu 2 satış şeklini farklı tablolarda tutuyorsunuz. Burada istediğiniz farklı tablolarda tutulsalar dahi satışların birbirini takip eden numaraları ID olarak alması.

Identity ile bunun yapılması mümkün değil. Çünkü Identity kolonuna table içinde kullanılmakta ve 1 table sınırlı olmaktaydı. Sequence ile bu isteği karşılamamız mümkün.

Bunun ötesinde aşağıdaki durumlarda Sequence Object kullanma ihtiyacınız doğabilir.

  • Insert’ten önce otomatik artan ID bilgisine ihtiyacınız varsa.
  • Otomatik artan sayıyı birden fazla tabloda ortak kullanmak istiyorsanız.
  • Belirli bir rakama erişildiğinde otomatik artan sayının başa dönmesi isteniyorsa.
  • Otomatik artan sayının artma miktarı gibi özelliklerini değiştirmek istiyorsanız.

Şimdi örnek kullanımlara geçelim.

2 Tablo için Ortak Sequence Object Kullanımı

Bu örneğimizde Internet Satışları ve Perakende Satışları için ortak bir otomatik artan sayı kullanmaya çalışacağız.

--Sequence Object'i oluşturalım
CREATE SEQUENCE SeqSalesID
    AS int
    START WITH 1
    INCREMENT BY 1 ;
GO

--Internet satışlarını tuttuğumuz tablo
CREATE TABLE InternetSales
(
    SalesID int PRIMARY KEY CLUSTERED 
        DEFAULT (NEXT VALUE FOR SeqSalesID),
    SalesDate date NOT NULL DEFAULT (getdate()),
    SalesAmount float NOT NULL
) ;
GO

--perakende satışları tuttuğumuz tablo
CREATE TABLE RetailerSales
(
    SalesID int PRIMARY KEY CLUSTERED 
        DEFAULT (NEXT VALUE FOR SeqSalesID),
    SalesDate date NOT NULL DEFAULT (getdate()),
    SalesAmount float NOT NULL
) ;
GO

--Satış yapalım
INSERT InternetSales (SalesDate, SalesAmount) 
    VALUES (GETDATE(), 150) ;
INSERT InternetSales (SalesDate, SalesAmount) 
    VALUES (GETDATE(), 350) ;
INSERT RetailerSales (SalesDate, SalesAmount) 
    VALUES (GETDATE(), 350) ;
INSERT InternetSales (SalesDate, SalesAmount) 
    VALUES (GETDATE(), 350) ;
INSERT RetailerSales (SalesDate, SalesAmount) 
    VALUES (GETDATE(), 550) ;


-- UNION ALL ile sorgu çekelim ve otomatik artan sayıya bakalım.
SELECT SalesID, SalesDate, SalesAmount FROM InternetSales
UNION ALL
SELECT SalesID, SalesDate, SalesAmount FROM RetailerSales
ORDER BY SalesID ;
GO

--Oluşturduğumuz objeleri drop edelim
DROP Table InternetSales
DROP Table RetailerSales
DROP SEQUENCE SeqSalesID

Sorgu sonucu şu şekilde olacaktır.

image_1

Gördüğünüz gibi SalesID 2 farklı tablo içinde birbirini takip etmekte.

Tekrarlayan Sequence Object

Bu örneğimizde 10 a geldiğinde tekrar başa dönen bir Sequence oluşturacağız.

CREATE SEQUENCE CountBy10
   AS tinyint
    START WITH 1
    INCREMENT BY 1
    MINVALUE 1
    MAXVALUE 10
    CYCLE ;
GO

SELECT NEXT VALUE FOR CountBy10 AS ID, Name FROM sys.objects ;
GO

DROP SEQUENCE CountBy10
GO

image_2

Gördüğünüz gibi otomatik artan sayı 10 a kadar sayıp tekrar başa dönüp 1 den başladı.

Sequence Object’i Resetlemek

Aşağıdaki kod ile Sequence Object’i drop-create etmeden istediğimiz bir numaradan tekrar başlatabiliriz. Bunu Identity için ReSeed komutuna benzetebiliriz.

ALTER SEQUENCE Samples.IDLabel
RESTART WITH 1 ;

Gördüğünüz gibi Sequence’i farklı amaçlar için kullanmak mümkün. Identity’e oranla daha fazla esneklik sağlayan Sequence Objesinin ilerleyen günlerde çok daha sıklıkla kullanılacağını düşünüyorum.

İyi Çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


SQL Server 11 “Denali” ile gelen yeni özelliklerden biride Query’lerde Paging ya da sayfalama yapabilme opsiyonu. Denali’den öncede bazı yöntemlerle paging yapılabilmekteydi ama yeni gelen syntax’la bu işlemi yapabilmek artık çok daha kolay.

[more]

İlk olarak Denali’den önce bu işlemin nasıl yapıldığına bakalım.

Örneğin aşağıdaki sorgu Satış tablosundaki verilerin 100 ile 150 arasındaki kayıtlarını getirmekte. Yani her sayfada 50 kayıt bulunuyorsa 3.sayfayı getirmeye çalışıyoruz.

SELECT * FROM (
SELECT *
    ,ROW_NUMBER() OVER (ORDER BY soh.SalesOrderID) AS 'RowNumber'
FROM Sales.SalesOrderHeader soh
) Tbl1
WHERE RowNumber>100 and RowNumber<=150

Denali’de ise şu syntax ile paging yapabilmekteyiz.

SELECT *
FROM Sales.SalesOrderHeader soh
ORDER BY soh.SalesOrderID ASC
    OFFSET 100 ROWS
    FETCH NEXT 50 ROWS ONLY;

Bir de bunu SP’ye dönüşterelim ve her sayfada kaç row var, kaçıncı sayfayı istiyoruz parametrelerini göndererek sorguyu çekelim.

CREATE PROCEDURE dbo.spGetSales
(
    @PageNumber int = 1,
    @RowCountInPage int = 50
)
AS
BEGIN
	SELECT *
	FROM Sales.SalesOrderHeader soh
	ORDER BY soh.SalesOrderID ASC
		OFFSET (@PageNumber-1)*@RowCountInPage ROWS
		FETCH NEXT @RowCountInPage ROWS ONLY;
END

SP’yi çağıralım. Her sayfada 50 kayıt var, 3. sayfayı almak istiyorum.

exec dbo.spGetSales 3,50

İyi çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan


Bugün size geçenlerde ihtiyacım olan bir konudan, varchar bir değişken içerisinde bulunan bir varbinary(64) değerin varbinary(64)’e convert edilmesi işleminden bahsedeceğim.

[more]

Aslında ne var bunda diyeceksiniz. Altı üstü convert komutuyla convertion’ı yapabildiğimizi düşüneceksiniz. Lakin konu o kadar basit değil. J

Örneklerle ne demek istediğimi anlatmaya çalışayım.

--varbinary(64) değerim string bir değişkenin içinde
declare @str varchar(100)='0x03000500462D95441BA30B01A69D00000100000000000000'
declare @vb varbinary(64)

--ben bu değerin aynı kalarak varbinary(64)'e dönüştürmek istiyorum.
set @vb=convert(varbinary(64),@str)
select @vb
--sonuç
--0x3078303330303035303034363244393534343142413330423031413639443030303030313030303030303030303030303030
--gördüğünüz gibi değer aynı kalmadı,
--varbinary(64)'e çevrildi.Oysa biz ben aynı kalmasını istiyorum.

--O zaman.....
set @vb=convert(varbinary(64),@str,1)
select @vb
--bingooooo :)
--trik nokta convert'in 3.parametresi olan "1".

 

İyi çalışmalar

Turgay Sahtiyan

Not : Blog haricinde, faydali gördügüm yazilari ve linkleri twitter adresimden paylasiyorum. Beni twitter'da takip etmek için : twitter.com/turgaysahtiyan