Rabu, 29 September 2010

Meningkatkan Performa Stored Procedure

Kita lebih sering menggunakan stored procedure dibandingkan query yang panjang pada arsitektur client-server. Client hanya akan mengirimkan nama stored procedure(dan parameter) ke server. Hal ini akan memperingan kinerja dari transfer antara client dan server serta menampik isu-isu keamanan(menyembunyikan object data).

Dengan seringnya pemakaian strored procedure, maka kita juga perlu memperhatikan pembuatan stored procedure agar semakin efektif. Berikut ada beberapa tips dalam pembuatan stored procedure:

1. Pergunakanlah statement SET NO COUNT ON
Ketika kita melakukan query SELECT atau DML pada sql maka SQL akan mengembalikan jumlah rows affected. Dengan SET NO COUNT ON, jumlah rows affected tidak akan muncul lagi. Hal ini akan mengurangi traffic pada network.

2. Pergunakanlah nama schema dan objectnya
Pada stored procedure mempergunakan nama schema sebelum kita melakukan query suatu object akan mempermudah SQL Server dalam mencari object yang ingin dituju. Dengan ini SQL Server akan mencari object lebih cepat.
contoh:
SELECT * FROM   dbo.mst_books 


3. Jangan mempergunakan awalan "SP_" pada nama stored procedure
Awalan "SP_" digunakan oleh nama stored procedure pada system. SQL Server akan mencari stored procedure dengan awalan "SP_" pada database master terlebih dahulu. Pencarian ini akan memakan waktu dan jika stored procedure yang dibuat oleh user sama dengan stored procedure pada database master, maka stored procedure yang dibuat oleh user tidak akan pernah dieksekusi.


4. Pergunakanlah stored procedure sp_executesql daripada statement EXECUTE
Menggunakan sp_executesql membuat dynamic query kita dapat digunakan kembali. Ketika kita mengubah parameter pada sp_executesql, SQL Server akan menggunakan execution plan yang sama dengan sebelumnya. Sebagai contoh :
DECLARE
 @Query VARCHAR(100) 
DECLARE @Age INT 

SET @Age = 19 
SET @Query = 'SELECT * FROM dbo.mst_mahasiswa WHERE Age = ' + 
             CONVERT(VARCHAR(3), @Age) 

EXEC (@Query)



Pada contoh diatas, query diatas tidak reusable. Ketika kita mengganti age dengan 20, SQL Server tidak akan menggunakan execution plan yang sama dengan query sebelumnya.


DECLARE @Query NVARCHAR(100)

SET @Query = N'SELECT * FROM dbo.mst_mahasiswa WHERE Age = @Age'

EXECUTE Sp_executesql @Query, N'@Age int', @Age = 19  

EXECUTE Sp_executesql @Query, N'@Age int', @Age = 20

Dengan sp_executesql, SQL Server akan menggunakan execution plan yang sama, dimana itu akan mempercepat proses kerja SQL.



5Pergunakanlah IF EXISTS(SELECT 1) daripada (SELECT *)
Untuk mengecek apakah ada suatu record dalam suatu table, lebih baik kita menggunakan IF EXISTS(SELECT 1) daripada IF EXISTS(SELECT *). Hasil dari internal query SELECT * akan berarti sama saja dengan SELECT 1, karena dua-duanya akan berarti true. Untuk meminimalis proses maka kita tidak memerlukan record set yang komplit. Contoh :



IF EXISTS (SELECT 1 
           FROM   sysobjects 
           WHERE  name = 'mst_mahasiswa' 
                  AND TYPE = 'U') 



6Hindari mempergunakan SQL Cursors 
Cursor menggunakan banyak resource dalam memposisikan record dalam record set dan ini sangat mengurangi performa. Jika kita ingin memproses record satu persatu dalam loop, kita bisa menggunakan kalusa WHILE. Contoh :



DECLARE @temp_table TABLE (
  id        INT IDENTITY(1, 1),
  policy_no VARCHAR(50))
DECLARE @counter INT
DECLARE @max_record INT

INSERT INTO @temp_table
            (policy_no)
SELECT TOP 10 pr_policyno
FROM   mst_rule

SELECT @max_record = COUNT(*)
FROM   @temp_table

SET @counter=1

WHILE @counter <= @max_record
  BEGIN
      SELECT *
      FROM   @temp_table
      WHERE  id = @counter

      SET @counter=@counter + 1
  END 



7Usahakan membuat transaksi sependek mungkin
Jika suatu transaksi memiliki kemampuan untuk mengunci suatu object maka dengan transaksi yang pendek, waktu lock pada suatu object di database akan pendek. Semakin panjang waktu transaksi, maka waktu lock pada suatu object juga akan semakin panjang. Dalam beberapa kasus, lock juga bisa menimbulkan deathlock.



8. Kembalikanlah integer value sebagai return value daripada mengembalikan integer yang merupakan bagian record set.
Menggunakan statement return integer akan mempercepat proses karena SQL tidak perlu membuat record set untuk dikembalikan ke client.


Semoga beberapa tips berikut membantu teman-teman dalam pembuatan stored procedure yang lebih cepat. ^_^

Selasa, 28 September 2010

Eksekusi Query berulang kali

Sesekali kita ingin mengeksekusi query secara berulang-ulang, entah itu untuk testing atau untuk keperluan mengumpulkan data. Pertama kali saya menggunakan metode looping(while) untuk melakukan query berulang-ulang. Sampai akhirnya saya menemukan suatu cara dengan syntax GO, berikut contohnya :

CREATE TABLE dbo.data 
  ( 
     id    INT IDENTITY (1, 1), 
     ratio DECIMAL(19, 3) 
  ) 

GO 

INSERT INTO dbo.data (ratio) VALUES(Rand()) 

GO 20 


Yah dengan query ini setidaknya saya menghemat waktu saya daripada membuat query looping. Selamat mencoba, thanks.

Jumat, 24 September 2010

Menyimpan script yang sering dipakai dengan Template Explorer (MSSQL 2005)


Ketika kita melakukan development atau monitoring sesuatu pekerjaan, kita secara tidak sadar menggunakan berulang script tertentu. Untuk memudahkan penyimpanan dan pemanggilan script tersebut kita dapat menyimpannya di Template Explorer. Template Explorer menampilkan SQL Script yang kita simpan dalam bentuk tree dan dapat dikelompokan.

Template Explorer sendiri secara default menyimpan SQL Script yang membantu kita untuk membuat object di database. Data-data dari template explorer disimpan di Users Documents and Settings\ "User Name"\Application Data\Microsoft\Microsoft SQL Server\100\Tools\Shell\Templates.
Terdapat beberapa template yang berguna untuk membuat object seperti database, table, view, index, stored procedure, trigger, statistic, dan function. Selain itu ada template yang berguna untuk memanage server dengan membuat properti tambahan seperti linked servers, login, roles, user, dan template untuk Analysis Services dan SQL Server Compact 3.5 SP1.

Untuk membuka Template Explorer bisa dipilih dari menu View -> Template Explorer atau dengan menekan Ctrl + Alt + T. Setelah Template Explorer terbuka, kita bisa klik kanan di "SQL Server Templates" dan klik New -> Folder atau New -> Template. Jangan lupa di save ya ^_^



Senin, 17 Mei 2010

My Shortcuts in SQL Server Management Studio(SSMS)

Didalam SSMS kita bisa membuat shortcuts sendiri untuk perintah-perintah yang sering kita lakukan. Perubahan shortcuts di SSMS bisa dibuka melalui menu Tools -> Options -> Keyboard. Berikut tampilannya :

Berikut adalah daftar shortcuts yang saya buat di SSMS : 

1.  Alt + F1
sp_help
Menampilkan informasi dari object database (semua object di sys.sysobjects, user define data type atau data type) . Contohnya kita blok suatu table atau stored procedure, lalu kita tekan Alt + F1 untuk menampilkan informasinya..

2.  Ctrl + F1
sp_helpText
Menampilkan definisi dari user-defined rule, default, Transact-SQL stored procedure yang tidak dienkripsi, user-defined Transact-SQL function, trigger, computed column, CHECK constraint, view, atau system object seperti system stored procedure. Contohnya kita blok suatu stored procedure, lalu kita tekan Ctrl + F1 untuk menampilkan definisi dari stored procedure tersebut.

3.  Ctrl + 1
sp_who
Menampilkan informasi tentang users, sessions, dan proses yang sedang menggunakan instance dari Microsoft SQL Server Database Engine. Cara menggunakannya, kita bisa tinggal tekan Ctrl + 1 pada layar query.

4.  Ctrl + 2
sp_lock
Menampilkan informasi tentang lock dari suatu session(spid).

5.  Ctrl + 3
SELECT TOP 100 * FROM 
Menampilkan 100 record dari suatu table. Cara menggunakannya, kita bisa blok suatu nama table kemudian tekan tombol Ctrl + 3. Shortcut ini saya gunakan agar mempermudah saya jika ingin melihat gambaran data suatu table, tanpa memberatkan server dengan select semua data.

6.  Ctrl + 4
sp_tables
Menampilkan list dari object yang bisa kita gunakan untuk query di environment yang sedang kita gunakan, misalnya table, view, dll. Biasa saya menggunakan sp_tables untuk mencari nama suatu table. Cara menggunakannya bisa dengan langsung tekan Ctrl + 4 atau bisa juga memblok suatu nama table yang ingin kita cari dengan menambahkan tanda ' dan % didepan dan diblakang( contoh : '%mst_penjualan%'), lalu kita tekan Ctrl + 4.


7.  Ctrl + 5

SELECT Object_name(object_id) name
FROM   sys.sql_modules
WHERE  definition LIKE   

Saya membuat query sendiri dari sql_modules untuk menampilkan stored procedure atau trigger yang memiliki suatu string tertentu pada definisinya. Contoh penggunaannya, kita bisa blok '%stringYangDicari%' kemudian tekan Ctrl + 5. Jangan lupa untuk membubuhkan tanda ' (petik satu) dan % (persen).

8.  Ctrl + 6
SELECT DISTINCT so.name
FROM   sysobjects so (nolock)
WHERE  so.TYPE = 'P'
       AND so.name LIKE


Shortcut ini saya buat sendiri untuk mencari stored procedure yang memiliki suatu string tertentu pada namanya.  Untuk shortcut yang satu ini, cara penggunaannya sama dengan shortcut Ctrl + 5. Yaitu dengan menambahkan tanda petik dan persen pada sebelum dan sesudah string yang ingin kita cari. Contoh penggunaannya, kita bisa blok '%stringYangDicari%' sebelum kita klik Ctrl + 6.

9. Ctrl + 7
SELECT table_name,
       
column_name,
       
data_type,
       
character_maximum_length
FROM   information_schema.columns
WHERE  column_name LIKE

Shortcut ini saya buat untuk mencari nama table jika kita cuma ingat nama kolomnya saja. Untuk shortcut yang satu ini, cara penggunaannya sama dengan shortcut Ctrl + 5. Yaitu dengan menambahkan tanda petik dan persen pada sebelum dan sesudah string yang ingin kita cari. Contoh penggunaannya, kita bisa blok '%kolomYangDicari%' sebelum kita klik Ctrl + 7.

Untuk semua shortcut yang kita buat akan berjalan pada layar query baru. Semoga dengan shortcut-shortcut ini kita bisa lebih cepat lagi dalam menyelesaikan suatu pekerjaan. Thanks.

Rabu, 21 April 2010

Menggabungkan isi field menjadi satu String di MS SQL 2005

Ketika kita membutuhkan data dari isi kolom menjadi satu string. Kita bisa menggunakan sintak for xml.
Contohnya saya akan menggabungkan isi dari field name pada sys.databases :
SELECT name
FROM   sys.databases
WHERE  database_id > 4 
Name
Temp
Liquibase
NorthWind
EMPLOYEES


Dengan query berikut kita bisa menggabungkan nama tersebut :
SELECT Stuff((SELECT ', ' + name
              
FROM   sys.databases
              
WHERE  database_id > 4
              
ORDER  BY name
              
FOR XML PATH('')), 1, 2, '') AS namelist; 

Namelist
EMPLOYEES, Liquibase, NorthWind, Temp


Bagaimana jika kita ingin menambahkan tanda kurung siku pada setiap nama seperti berikut: <EMPLOYEES>, <Liquibase>, <NorthWind>, <Temp>

Jika kita menggunakan syntax seperti diatas :

SELECT Stuff((SELECT ', <' + name + '>'
              
FROM   sys.databases
              
WHERE  database_id > 4
              
ORDER  BY name
              
FOR XML PATH('')), 1, 2, '') AS namelist; 

Hasilnya tanda < dan > muncul menjadi &lt dan &gt :

namelist
&lt;EMPLOYEES&gt;, &lt;Liquibase&gt;, &lt;NorthWind&gt;, &lt;Temp&gt;


Untuk mengatasi itu, kita bisa menambahkan sedikit syntak forxml nya. Berikut querynya :

SELECT
STUFF(
(SELECT ', <' + name + '>'
FROM sys.databases
WHERE database_id > 4
ORDER BY name
FOR XML PATH(''), root('MyString'), TYPE
).value('/MyString[1]','varchar(max)')
, 1, 2, '') AS namelist;

atau

SELECT
STUFF(
(SELECT ', <' + name + '>'
FROM sys.databases
WHERE database_id > 4
ORDER BY name
FOR XML PATH(''), TYPE
).value('(./text())[1]','varchar(max)')
, 1, 2, '') AS namelist

Hasilnya :

namelist
<EMPLOYEES>, <Liquibase>, <NorthWind>, <Temp>

Selasa, 20 April 2010

Query Ranking dalam MS SQL Server 2005

Ranking atau peringkat, manusia senang membuat ranking. Dalam MS SQL Server 2005 pun tersedia beberapa fungsi untuk menampilkan hasil query dengan nomor ranking. Untuk contoh-contoh query ranking saya menggunakan Entity Relationship berikut :



Saya membuat view berikut untuk menjumlahkan SubTotal dari setiap order ID :
CREATE VIEW [dbo].[viewSumSubTotalPerOrder]
AS
  
SELECT orderid,
         
SUM(subtotal) AS total
  
FROM   dbo.orderitems
  
GROUP  BY orderid 

Untuk menampilkan query ranking, kita bisa menggunakan query ini :
SELECT c.name,
       
o.dateordered,
       
o.orderid,
       
vw.total,
       
Row_number() OVER (ORDER BY total DESC) AS bestcustomer
FROM   viewsumsubtotalperorder AS vw
       
INNER JOIN orders AS o
         
ON o.orderid = vw.orderid
       
INNER JOIN customers AS c
         
ON c.customerid = o.customerid 

Akan menghasilkan :
Name DateOrdered orderId Total BestCustomer
Kurniawan 00:00.0 11 450000 1
Slamet 00:00.0 6 240000 2
Kurniawan 00:00.0 10 220000 3
Rangky 00:00.0 3 100000 4
Firman 00:00.0 8 60000 5
Rangky 00:00.0 1 45000 6
Rico 00:00.0 2 45000 7
Slamet 00:00.0 5 40000 8
Fajar 00:00.0 7 30000 9
Firman 00:00.0 9 15000 10
Rico 00:00.0 4 2000 11

Yang ini query ranking yang dibagi-bagi berdasarkan nama customernya :
SELECT c.name,
       o.dateordered,
       o.orderid,
       vw.total,
       Row_number() OVER (PARTITION BY c.customerid ORDER BY total DESC) AS bestcustomer
FROM   viewsumsubtotalperorder AS vw
       INNER JOIN orders AS o
         ON o.orderid = vw.orderid
       INNER JOIN customers AS c
         ON c.customerid = o.customerid 

Menghasilkan :
Name DateOrdered orderId Total BestCustomer
Rico 00:00.0 2 45000 1
Rico 00:00.0 4 2000 2
Rangky 00:00.0 3 100000 1
Rangky 00:00.0 1 45000 2
Fajar 00:00.0 7 30000 1
Slamet 00:00.0 6 240000 1
Slamet 00:00.0 5 40000 2
Firman 00:00.0 8 60000 1
Firman 00:00.0 9 15000 2
Kurniawan 00:00.0 11 450000 1
Kurniawan 00:00.0 10 220000 2

Tapi kurangnya adalah, hasil ranking ini tidak bisa di masukan didalam kondisi "where" ataupun didalam expresi "order by". Untuk melakukan itu kita harus memasukan dulu ketable terpisah atau masukannya keview.
Berikut contoh querynya :
CREATE VIEW [dbo].[viewBestCustomers]
AS
  
SELECT c.name,
         
o.dateordered,
         
o.orderid,
         
vw.total,
         
Row_number() OVER (ORDER BY total DESC) AS bestcustomer
  
FROM   viewsumsubtotalperorder AS vw
         
INNER JOIN orders AS o
           
ON o.orderid = vw.orderid
         
INNER JOIN customers AS c
           
ON c.customerid = o.customerid

SELECT name,
       dateordered,
       orderid,
       total,
       bestcustomer
FROM   viewbestcustomers
WHERE  bestcustomer BETWEEN 3 AND 5 

Name DateOrdered orderId Total BestCustomer
Kurniawan 00:00.0 10 220000 3
Rangky 00:00.0 3 100000 4
Firman 00:00.0 8 60000 5

Semua hasil diatas dihasilkan dengan syntax ROW_NUMBER(), selanjutnya saya akan menampilkan perbedaan dengan menggunakan syntax RANK() dan DENSE_RANK :

Menggunakan RANK() :

SELECT c.name,
       o.dateordered,
       vw.total,
       Rank() OVER (ORDER BY total DESC) AS bestcustomer
FROM   viewsumsubtotalperorder AS vw
       INNER JOIN orders AS o
         ON o.orderid = vw.orderid
       INNER JOIN customers AS c
         ON c.customerid = o.customerid 

Menghasilkan :
Name DateOrdered Total BestCustomer
Kurniawan 00:00.0 450000 1
Slamet 00:00.0 240000 2
Kurniawan 00:00.0 220000 3
Rangky 00:00.0 100000 4
Firman 00:00.0 60000 5
Rangky 00:00.0 45000 6
Rico 00:00.0 45000 6
Slamet 00:00.0 40000 8
Fajar 00:00.0 30000 9
Firman 00:00.0 15000 10
Rico 00:00.0 2000 11

Menggunakan DENSE_RANK :
SELECT c.name,
       o.dateordered,
       vw.total,
       Dense_rank() OVER (ORDER BY total DESC) AS bestcustomer
FROM   viewsumsubtotalperorder AS vw
       INNER JOIN orders AS o
         ON o.orderid = vw.orderid
       INNER JOIN customers AS c
         ON c.customerid = o.customerid 

Menghasilkan :
Name DateOrdered Total BestCustomer
Kurniawan 00:00.0 450000 1
Slamet 00:00.0 240000 2
Kurniawan 00:00.0 220000 3
Rangky 00:00.0 100000 4
Firman 00:00.0 60000 5
Rangky 00:00.0 45000 6
Rico 00:00.0 45000 6
Slamet 00:00.0 40000 7
Fajar 00:00.0 30000 8
Firman 00:00.0 15000 9
Rico 00:00.0 2000 10

Yang terakhir adalah, query untuk membagi hasil ranking berdasarkan berapa grup yang kita mau :

SELECT productid,
       name,
       price,
       Ntile(4) OVER (ORDER BY price DESC) AS quartile
FROM   products 

Menghasilkan :
ProductID Name Price Quartile
3 Table 100000 1
6 Shoes 40000 1
2 Chair 30000 2
5 Bag 20000 2
7 Hat 15000 3
4 Pen 2000 3
1 Book 1500 4
8 Pencil 500 4

Bagi yang mau mencoba-coba, saya sertakan juga data mentahnya disini. File yang saya upload berextensi .doc, ubah saja menjadi .sql.

Semoga tutorial sederhana ini membantu dalam pembuatan report, atau query sederhana yang anda inginkan.