Variabel SQL untuk menyimpan daftar bilangan bulat

175

Saya mencoba men-debug laporan SQL orang lain dan telah menempatkan kueri laporan yang mendasarinya ke jendela kueri SQL 2012.

Salah satu parameter yang diminta oleh laporan adalah daftar bilangan bulat. Hal ini dicapai pada laporan melalui kotak tarik turun multi-pilih. Kueri yang mendasari laporan menggunakan daftar integer ini dalam wheremisalnya klausa

select *
from TabA
where TabA.ID in (@listOfIDs)

Saya tidak ingin mengubah kueri yang saya debug, tetapi saya tidak tahu cara membuat variabel pada SQL Server yang dapat menampung jenis data ini untuk mengujinya.

misalnya

declare @listOfIDs int
set listOfIDs  = 1,2,3,4

Tidak ada tipe data yang dapat menyimpan daftar bilangan bulat, jadi bagaimana cara menjalankan kueri laporan di SQL Server saya dengan nilai yang sama dengan laporan?

ErickTreetops
sumber
1
Saya tahu saya telah menggunakan Parmeter Nilai Tabel TVP untuk memasukkan data tetapi sekarang yakin apakah itu dapat digunakan di suatu tempat. Sekuel?
paparazzo
2
pertanyaan yang baik. +1
Ray Tanpa Cinta

Jawaban:

224

Variabel tabel

declare @listOfIDs table (id int);
insert @listOfIDs(id) values(1),(2),(3);    

select *
from TabA
where TabA.ID in (select id from @listOfIDs)

atau

declare @listOfIDs varchar(1000);
SET @listOfIDs = ',1,2,3,'; --in this solution need put coma on begin and end

select *
from TabA
where charindex(',' + CAST(TabA.ID as nvarchar(20)) + ',', @listOfIDs) > 0
Slavoo
sumber
2
Terima kasih untuk itu, tetapi sekali lagi saya harus menulis ulang cara variabel dibaca dalam kueri. Saya harus tetap sama.
ErickTreetops
3
Bagaimana jika Anda tidak tahu ID itu dan apa yang berasal dari kueri? Contoh: SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)Kueri ini akan mengembalikan beberapa ID dan saya mendapatkan kesalahan mengatakan subquery mengembalikan lebih dari satu hasil dan itu tidak diizinkan. Apakah ada pula untuk membuat variabel yang akan menyimpan array jika ID dari subquery?
Rafael Moreira
1
Saya mencoba opsi kedua dan berfungsi untuk lebih sedikit jumlah catatan. Ketika saya meningkatkan jumlah Id, saya mendapatkan kesalahan TimeOut. Mungkin para pemain menghambat kinerja.
Pengguna M
Bukan jawaban untuk pertanyaan asli tapi itu jawaban untuk pertanyaan yang tidak saya tanyakan, jadi saya baik-baik saja. Saya meneruskan Daftar <int> sebagai parameter, tetapi ingin membuat variabel lokal untuk pengujian dalam SSMS. Itu pembunuh.
Wade Hatler
Jawaban yang bagus, menyelamatkan saya banyak waktu
Alfredo A.
35

Dengan asumsi variabel adalah sesuatu yang mirip dengan:

CREATE TYPE [dbo].[IntList] AS TABLE(
[Value] [int] NOT NULL
)

Dan Prosedur Tersimpan menggunakannya dalam bentuk ini:

ALTER Procedure [dbo].[GetFooByIds]
    @Ids [IntList] ReadOnly
As 

Anda dapat membuat IntList dan memanggil prosedur seperti ini:

Declare @IDs IntList;
Insert Into @IDs Select Id From dbo.{TableThatHasIds}
Where Id In (111, 222, 333, 444)
Exec [dbo].[GetFooByIds] @IDs

Atau jika Anda menyediakan IntList sendiri

DECLARE @listOfIDs dbo.IntList
INSERT INTO @listofIDs VALUES (1),(35),(118);
William Mueller
sumber
17

Anda benar, tidak ada datatype di SQL-Server yang dapat menampung daftar bilangan bulat. Tetapi yang dapat Anda lakukan adalah menyimpan daftar bilangan bulat sebagai string.

DECLARE @listOfIDs varchar(8000);
SET @listOfIDs = '1,2,3,4';

Anda kemudian dapat membagi string menjadi nilai integer yang terpisah dan memasukkannya ke dalam tabel. Prosedur Anda mungkin sudah melakukan ini.

Anda juga dapat menggunakan kueri dinamis untuk mencapai hasil yang sama:

DECLARE @SQL nvarchar(8000);

SET @SQL = 'SELECT * FROM TabA WHERE TabA.ID IN (' + @listOfIDs + ')';
EXECUTE (@SQL);
Moz
sumber
Terima kasih, tetapi sekali lagi perlu memodifikasi kueri yang tidak diizinkan.
ErickTreetops
2
Jika ada yang menggunakan ini, perlu diketahui bahwa ini bisa sangat rentan terhadap injeksi SQL jika @listOfIDs adalah parameter string yang disediakan oleh pengguna. Bergantung pada arsitektur aplikasi Anda, ini bisa atau tidak bisa menjadi masalah.
Rogala
@Rogala Setuju, pengguna perlu melakukan sanitasi sendiri sesuai kebutuhan.
Möoz
1
@Moz Saya sarankan menambahkan catatan pada jawaban Anda untuk mencerminkan hal ini. Tidak semua orang tahu, dan mereka hanya menyalin dan menempelkan solusi tanpa memikirkan konsekuensinya. Dynamic SQL SANGAT berbahaya, dan saya menghindarinya seperti wabah itu.
Rogala
@Moz Juga, saya tidak memilih jawaban Anda, tetapi apa yang Anda harap luangkan waktu beberapa menit dan periksa jawaban saya? Fungsi STRING_SPLIT cukup manis, dan saya pikir Anda SEMUA akan mengatasinya !!
Rogala
6

Untuk SQL Server 2016+ dan Azure SQL Database, fungsi STRING_SPLIT ditambahkan yang akan menjadi solusi sempurna untuk masalah ini. Berikut ini dokumentasinya: https://docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

Berikut ini sebuah contoh:

/*List of ids in a comma delimited string
  Note: the ') WAITFOR DELAY ''00:00:02''' is a way to verify that your script 
        doesn't allow for SQL injection*/
DECLARE @listOfIds VARCHAR(MAX) = '1,3,a,10.1,) WAITFOR DELAY ''00:00:02''';

--Make sure the temp table was dropped before trying to create it
IF OBJECT_ID('tempdb..#MyTable') IS NOT NULL DROP TABLE #MyTable;

--Create example reference table
CREATE TABLE #MyTable
([Id] INT NOT NULL);

--Populate the reference table
DECLARE @i INT = 1;
WHILE(@i <= 10)
BEGIN
    INSERT INTO #MyTable
    SELECT @i;

    SET @i = @i + 1;
END

/*Find all the values
  Note: I silently ignore the values that are not integers*/
SELECT t.[Id]
FROM #MyTable as t
    INNER JOIN 
        (SELECT value as [Id] 
        FROM STRING_SPLIT(@listOfIds, ',')
        WHERE ISNUMERIC(value) = 1 /*Make sure it is numeric*/
            AND ROUND(value,0) = value /*Make sure it is an integer*/) as ids
    ON t.[Id] = ids.[Id];

--Clean-up
DROP TABLE #MyTable;

Hasil kueri adalah 1,3

~ Ceria

Rogala
sumber
4

Pada akhirnya saya sampai pada kesimpulan bahwa tanpa mengubah cara kerja kueri saya tidak bisa menyimpan nilai-nilai dalam variabel. Saya menggunakan SQL profiler untuk menangkap nilai dan kemudian mengkodekannya ke dalam kueri untuk melihat cara kerjanya. Ada 18 array integer ini dan beberapa memiliki lebih dari 30 elemen di dalamnya.

Saya berpikir bahwa ada kebutuhan untuk MS / SQL untuk memperkenalkan beberapa tipe data tambahan ke dalam bahasa. Array sangat umum dan saya tidak melihat mengapa Anda tidak dapat menggunakannya dalam proc yang disimpan.

ErickTreetops
sumber
6
SQL Server tidak perlu array, ketika memiliki parameter dan variabel bernilai tabel.
John Saunders
1
Jadi yang kita tahu adalah bahwa kueri menggunakan daftar bilangan bulat (diteruskan oleh array?). Yang tidak saya mengerti adalah bagaimana kueri Anda menggunakannya tanpa menggunakan salah satu metode yang diberikan dalam jawaban. Berikan lebih banyak konteks dan kami dapat membantu Anda lebih jauh.
Möoz
2

Anda tidak bisa melakukannya seperti ini, tetapi Anda bisa menjalankan seluruh kueri yang menyimpannya dalam sebuah variabel.

Sebagai contoh:

DECLARE @listOfIDs NVARCHAR(MAX) = 
    '1,2,3'

DECLARE @query NVARCHAR(MAX) = 
    'Select *
     From TabA
     Where TabA.ID in (' + @listOfIDs + ')'

Exec (@query)
thepirat000
sumber
2
Seperti yang dinyatakan dalam komentar sebelumnya, tergantung pada bagaimana Anda menerapkan jenis solusi ini, ketahuilah bahwa ini bisa rentan terhadap injeksi SQL jika @listOfIDs adalah parameter yang disediakan oleh pengguna.
Rogala
2

Ada fungsi baru dalam SQL dipanggil string_splitjika Anda menggunakan daftar string. Tautan Ref STRING_SPLIT (Transact-SQL)

DECLARE @tags NVARCHAR(400) = 'clothing,road,,touring,bike'
SELECT value
FROM STRING_SPLIT(@tags, ',')
WHERE RTRIM(value) <> '';

Anda dapat mengirimkan permintaan ini dengan insebagai berikut:

SELECT *
  FROM [dbo].[yourTable]
  WHERE (strval IN (SELECT value FROM STRING_SPLIT(@tags, ',') WHERE RTRIM(value) <> ''))
Ravi Anand
sumber
1
Tampaknya masuk akal tetapi hanya SQL Server 2016 dan seterusnya yang sayangnya.
AnotherFineMess
0

Saya menggunakan ini:

1-Deklarasikan variabel tabel temp di skrip gedung Anda:

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2-Alokasikan ke tabel temp:

IF (SOME CONDITION) 
BEGIN 
        INSERT INTO @ShiftPeriodList SELECT ShiftId FROM [hr].[tbl_WorkShift]
END
IF (SOME CONDITION2)
BEGIN
    INSERT INTO @ShiftPeriodList
        SELECT  ws.ShiftId
        FROM [hr].[tbl_WorkShift] ws
        WHERE ws.WorkShift = 'Weekend(VSD)' OR ws.WorkShift = 'Weekend(SDL)'

END

3-Referensi tabel ketika Anda membutuhkannya dalam pernyataan WHERE:

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
Max Alexander Hanna
sumber