Mengapa saya tidak bisa menggunakan variabel dalam T-SQL seperti yang saya bayangkan saya bisa?

18

Maafkan saya, saya adalah seorang pengembang yang telah pindah ke dunia SQL. Saya pikir saya bisa meningkatkan beberapa SQL dengan menambahkan variabel tetapi tidak berfungsi seperti yang saya harapkan. Dapatkah seseorang memberi tahu saya mengapa ini tidak berhasil? Saya tidak ingin bekerja di sekitar, saya ingin tahu alasan mengapa ini tidak berfungsi seperti yang saya bayangkan seharusnya karena saya yakin ada alasan yang bagus, tetapi saat ini tidak melompat ke arah saya.

DECLARE @DatabaseName varchar(150)
SET @DatabaseName = 'MyAmazingDatabaseName'

CREATE DATABASE @DatabaseName
GO

USE @DatabaseName
GO
Gareth
sumber
Anda perlu menggunakan sql dinamis untuk itu. mssqltips.com/sqlservertip/1160/…
Stijn Wynants
3
Hai, terima kasih sudah berkomentar tapi sesuai pertanyaan saya, ini bukan jawaban yang saya cari. Saya ingin tahu apakah ada yang tahu mengapa saya tidak bisa melakukannya seperti yang saya tunjukkan.
Gareth
2
Karena tidak mungkin menggunakan USEperintah dengan parameter.
TT.
2
Selain jawaban yang sudah diberikan, tetapi tidak cukup untuk jawaban sendiri, variabel dicakup hingga maksimum dari kumpulan saat ini. (Saya tidak tahu jika mungkin untuk lingkup variabel lebih sempit dari itu di SQL Server.) Jadi saat Anda memiliki GO, variabel yang dinyatakan sebelumnya menghilang. Anda mungkin ingin melihat variabel SQLCMD, yang mungkin atau mungkin tidak berlaku untuk skenario Anda.
CVn
1
Kita cenderung menganggap nama database dan nama kolom sebagai nilai string, tetapi dalam konteks SQL mereka adalah Pengidentifikasi. Apa yang Anda coba akan sama dengan mengharapkan x = 7; sama dengan 'x' = 7; dalam beberapa bahasa lain. Sama seperti bahasa komputer dapat dibuat yang berkaitan dengan 'x' = 7 sama dengan x = 7, RDBMS dapat dibuat yang memperlakukan Buat Tabel X sama dengan Buat Tabel 'X'. Tapi itu bukan SQL.
user1008646

Jawaban:

20

Per halaman online Buku untuk variabel

Variabel hanya dapat digunakan dalam ekspresi, bukan sebagai pengganti nama objek atau kata kunci. Untuk membangun pernyataan SQL dinamis, gunakan EXECUTE.

Ini akan bekerja seperti yang Anda harapkan jika, misalnya, Anda menggunakan variabel Anda dalam klausa di mana. Adapun alasannya, saya akan berpikir itu ada hubungannya dengan parser tidak dapat mengevaluasi variabel dan dengan demikian memeriksa keberadaan. Saat mengeksekusi, kueri diuraikan terlebih dahulu untuk sintaks dan objek dan kemudian, jika penguraian berhasil, kueri dieksekusi pada titik mana variabel akan ditetapkan.

DECLARE @name varchar(20);
SET @name = 'test';

CREATE TABLE [#tmp]([val] varchar(10));

insert into #tmp
values('test')

SELECT *
FROM [#tmp]
WHERE [val] = @name;
Bob Klimes
sumber
3
Perhatikan bahwa SQL dinamis harus dihindari sedapat mungkin. Ini analog SQL untuk menggunakan evalfungsi dalam bahasa prosedural seperti JavaScript dan Python. Ini adalah cara cepat untuk membuat lubang keamanan.
jpmc26
1
@ jpmc26: Apa cara yang lebih aman untuk melakukannya yang tidak melibatkan SQL dinamis?
Robert Harvey
1
@RobertHarvey Hanya karena sebaiknya dihindari tidak berarti selalu ada alternatif dengan fungsi yang persis sama. ;) Sering kali, bagian dari jawabannya adalah, "Gunakan solusi yang sama sekali berbeda untuk masalah ini." Kadang-kadang itu adalah hal terbaik untuk dilakukan, tetapi bukan tanpa banyak pertimbangan dan memastikan Anda belum mengabaikan alternatif, dan bahkan kemudian, itu harus datang dengan dosis hati-hati yang sehat.
jpmc26
2
@ jpmc26: Contoh OP terlihat seperti apa yang mungkin dilakukan ORM "Code-First" untuk mengatur tabel dalam database. Sementara SQL dinamis pada prinsipnya tidak aman, pengguna akhir tidak akan pernah menyentuh kode tertentu itu.
Robert Harvey
@RobertHarvey Tergantung siapa yang Anda anggap "pengguna akhir." Untuk skrip yang menggunakan DB, saya akan mempertimbangkan pengembang dan mungkin beberapa sys admin sebagai "pengguna akhir." Saya masih mendesain ke sistem untuk menolak input tidak aman dalam kasus itu, jika tanpa alasan selain untuk menghindari kecelakaan. Juga, seperti untuk "tidak pernah menyentuh," OP menyentuh kode ini, jadi ...
jpmc26
17

Keterbatasan penggunaan variabel dalam pernyataan SQL muncul dari arsitektur SQL.

Ada tiga fase dalam pemrosesan pernyataan SQL:

  1. Persiapan - Pernyataan diuraikan dan rencana eksekusi dikompilasi, menentukan objek database mana yang diakses, bagaimana mereka diakses dan bagaimana mereka terkait. Paket eksekusi disimpan dalam cache paket .
  2. Binding - variabel apa pun dalam pernyataan diganti dengan nilai aktual.
  3. Eksekusi - rencana cache dieksekusi dengan nilai terikat.

SQL server menyembunyikan langkah persiapan dari programmer dan menjalankannya jauh lebih cepat daripada database tradisional seperti Oracle dan DB2. Karena alasan kinerja, SQL menghabiskan banyak waktu untuk menentukan rencana eksekusi optimal, tetapi hanya melakukannya saat pertama kali pernyataan tersebut ditemui setelah restart.

Jadi dalam SQL statis , variabel hanya dapat digunakan di tempat-tempat di mana mereka tidak akan membatalkan rencana eksekusi, jadi tidak untuk nama tabel, nama kolom (termasuk nama kolom dalam kondisi WHERE), dll.

SQL dinamis ada untuk kasus-kasus di mana seseorang tidak dapat bekerja di sekitar batasan, dan programmer tahu bahwa itu akan memakan waktu sedikit lebih lama untuk dieksekusi. SQL dinamis bisa rentan terhadap injeksi kode berbahaya, jadi berhati-hatilah!

grahamj42
sumber
7

Seperti yang Anda lihat, pertanyaan "mengapa" membutuhkan jawaban yang berbeda, termasuk pemikiran historis dan asumsi yang mendasari bahasa tersebut, saya tidak yakin saya benar-benar dapat melakukan keadilan itu.

Artikel komprehensif ini oleh SQL MVP Erland Sommarskog tidak mencoba memberikan beberapa alasan, bersama dengan mekanika:

Kutukan dan Berkat dari Dynamic SQL :

Paket Permintaan Caching

Setiap kueri yang Anda jalankan di SQL Server membutuhkan rencana kueri. Ketika Anda menjalankan kueri pertama kali, SQL Server membangun rencana kueri untuk itu - atau seiring terminologi - mengkompilasi kueri. SQL Server menyimpan paket dalam cache, dan lain kali Anda menjalankan kueri, paket tersebut digunakan kembali.

Ini (dan keamanan, lihat di bawah) mungkin merupakan alasan terbesar.

SQL beroperasi di bawah premis bahwa query bukan operasi satu kali, tetapi mereka akan digunakan berulang kali. Jika tabel (atau database!) Sebenarnya tidak ditentukan dalam kueri, itu tidak memiliki cara untuk menghasilkan dan menyimpan rencana eksekusi untuk digunakan di masa depan.

Ya, tidak setiap kueri yang kami jalankan akan digunakan kembali, tetapi ini adalah premis operasi standar dari SQL , jadi "pengecualian" dimaksudkan untuk menjadi luar biasa.

Beberapa alasan lain daftar Erland (perhatikan bahwa ia secara eksplisit mencantumkan keuntungan menggunakan prosedur yang tersimpan , tetapi banyak di antaranya juga keuntungan dari kueri parameterisasi (non-dinamis)):

  • Sistem Izin : mesin SQL tidak dapat memprediksi apakah Anda memiliki hak untuk menjalankan kueri jika tidak tahu tabel (atau database) yang akan Anda gunakan untuk melawannya. "Rantai izin" menggunakan SQL dinamis adalah rasa sakit di pantat.
  • Mengurangi Lalu Lintas Jaringan : Melewati nama proc yang disimpan dan beberapa nilai parameter di jaringan lebih pendek daripada pernyataan kueri yang panjang.
  • Encapsulating Logic : Anda harus terbiasa dengan keuntungan dari enkapsulasi logika dari lingkungan pemrograman lain.
  • Melacak Apa yang Digunakan : Jika saya perlu mengubah definisi kolom, bagaimana saya bisa menemukan semua kode yang memanggilnya? Prosedur sistem ada untuk menemukan dependensi dalam database SQL, tetapi hanya jika kode tersebut dalam prosedur tersimpan.
  • Kemudahan Menulis Kode SQL : Pemeriksaan sintaksis terjadi ketika Anda membuat atau memodifikasi prosedur yang tersimpan, jadi semoga lebih sedikit kesalahan yang terjadi.
  • Mengatasi Bug dan Masalah : DBA dapat melacak dan mengukur kinerja masing-masing prosedur tersimpan jauh lebih mudah daripada SQL dinamis yang selalu berubah.

Sekali lagi, masing-masing memiliki seratus nuansa yang tidak akan saya bahas di sini.

BradC
sumber
2

Anda harus menggunakan sql dinamis

DECLARE @DatabaseName varchar(150) = 'dbamaint'
declare @sqltext nvarchar(max) = N''

set @sqltext = N'CREATE DATABASE '+quotename(@DatabaseName)+ ';'

print @sqltext 

-- once you are happy .. uncomment below
--exec sp_executesql @sqltext
set @sqltext = ''
set @sqltext = N'use '+quotename(@DatabaseName)+ ';'
print @sqltext 
-- once you are happy .. uncomment below
--exec sp_executesql @sqltext

di bawah ini adalah hasil cetak .. sekali Anda menghapus komentar exec sp_executesql @sqltextpernyataan tersebut akan benar-benar dieksekusi ...

CREATE DATABASE [dbamaint];
use [dbamaint];
Kin Shah
sumber
1
Ya terima kasih, saya tahu itu, tetapi saya ingin tahu apakah ada yang tahu mengapa Anda tidak bisa menggunakan variabel saja?
Gareth
Parser T-SQL akan melempar kesalahan sintaksis. Ini bukan T-SQL yang valid yang diakui parser.
Kin Shah
Terima kasih Kin, saya yakin pasti ada alasan bagus untuk itu. Mungkin karena nama basis data dapat berisi '@' dan mungkin beberapa alasan lain yang lebih kompleks.
Gareth
1
Ya, mereka dapat mengandung @ dan saya pikir itu alasan utama. msdn.microsoft.com/en-us/library/ms175874.aspx
Paweł Tajs
1
@gazeranco Percayalah, siapa pun yang bekerja dengan SQL Server pasti berharap lebih banyak perintah akan menerima variabel menggantikan pengidentifikasi konstan.
db2