Tamsayıların listesini tutmak için SQL değişkeni


176

Başkasının SQL raporlarında hata ayıklamaya çalışıyorum ve SQL 2012 sorgu pencerelerine temel raporlar sorgusu yerleştirdik.

Raporun istediği parametrelerden biri tamsayıların listesidir. Bu, raporda çoklu seçim açılır kutusu aracılığıyla elde edilir. Raporun altında yatan sorgu, bu tamsayı listesini whereyan tümcede kullanır;

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

Hata ayıklama yaptığım sorguyu değiştirmek istemiyorum, ancak SQL Server'da bu tür verileri test etmek için bu tür verileri tutabilen bir değişken oluşturmayı anlayamıyorum.

Örneğin

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

Tamsayıların listesini tutabilecek hiçbir veri türü olmadığından, SQL Server'ımda rapor sorgusunu raporla aynı değerlerle nasıl çalıştırabilirim?


1
Veri eklemek için TVP Tablo Değeri Parametresi kullandığımı biliyorum ama şimdi bir yerde kullanıp kullanamayacağından eminim. Devamı?
paparazzo

2
iyi ifade edilmiş bir soru. +1
RayLoveless

Yanıtlar:


224

Tablo değişkeni

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

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

veya

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

2
Bunun için teşekkürler ama yine değişkenin sorguda okunma şeklini yeniden yazmam gerekiyor. Aynı tutmak zorundayım.
ErickTreetops

3
Kimliklerin ne olduğunu bilmiyorsanız ve bu bir sorgudan gelirse ne olur? Örnek: SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)Bu sorgu birden çok kimlik döndürür ve alt sorgunun birden fazla sonuç verdiğini ve buna izin verilmediğini söyleyerek bir hata alıyorum. Alt sorgu kimlikleri varsa dizi saklayacak bir değişken oluşturmak için yine de var mı?
Rafael Moreira

1
İkinci seçeneği denedim ve daha az sayıda kayıt için çalışıyor. ID sayısını artırdığımda TimeOut hatası alıyorum. Belki oyuncular performansı engelliyor.
Kullanıcı M

Orijinal sorunun cevabı değil ama sormadım bir cevap, bu yüzden iyiyim. <int> Listesini parametre olarak iletiyorum, ancak SSMS'de sınamak için yerel bir değişken yapmak istiyorum. Katil.
Wade Hatler

Harika cevap, çok zaman
Alfredo A.

35

Değişkenin varsayılması şuna benzer:

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

Saklı Yordam bunu şu biçimde kullanır:

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

IntList oluşturabilir ve prosedürü şöyle çağırabilirsiniz:

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

Veya IntList'e kendiniz veriyorsanız

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

17

Haklısınız, SQL Server'da tamsayıların listesini tutabilecek bir veri türü yok. Ancak yapabileceğiniz şey, tamsayıların bir listesini dize olarak saklamaktır.

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

Daha sonra dizeyi ayrı tamsayı değerlerine bölebilir ve bir tabloya yerleştirebilirsiniz. Prosedürünüz zaten bunu yapabilir.

Aynı sonucu elde etmek için dinamik bir sorgu da kullanabilirsiniz:

DECLARE @SQL nvarchar(8000);

SET @SQL = 'SELECT * FROM TabA WHERE TabA.ID IN (' + @listOfIDs + ')';
EXECUTE (@SQL);

Teşekkürler, ancak tekrar izin verilmiyor bir sorguyu değiştirmek gerekir.
ErickTreetops

2
Birisi bunu kullanıyorsa, @listOfIDs bir kullanıcı tarafından sağlanan bir dize parametresi ise bunun SQL enjeksiyonuna karşı çok savunmasız olabileceğini lütfen unutmayın. Uygulamanızın mimarisine bağlı olarak, bu bir sorun olabilir veya olmayabilir.
Rogala

@Rogala, kullanıcıların gerektiği gibi kendi sağlık önlemlerini almaları gerektiğini kabul etti.
Möoz

1
@ Möoz Bunu yansıtmak için cevabınıza bir not eklemenizi tavsiye ederim. Herkes bilmiyor ve sonuçları düşünmeden çözümleri kopyalayıp yapıştırıyorlar. Dinamik SQL ÇOK tehlikelidir ve ben veba gibi önlemek.
Rogala

@ Möoz Ayrıca, cevabınızı aşağıya oylamadım, ama lütfen birkaç dakikanızı ayırın ve cevabımı kontrol edin. STRING_SPLIT işlevi oldukça tatlı ve sanırım TÜMÜ üzerinde olacaksın !!
Rogala

6

SQL Server 2016+ ve Azure SQL Veritabanı için, bu sorun için mükemmel bir çözüm olabilecek STRING_SPLIT işlevi eklendi. İşte belgeler: https://docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

İşte bir örnek:

/*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;

Sorgunun sonucu 1,3

~ Alkış


4

Sonunda ben sorgu nasıl çalışır değiştirmeden değerleri değişkenleri saklayamadı sonucuna geldi. Değerleri yakalamak için SQL profiler kullandım ve sonra nasıl çalıştığını görmek için onları sorguya kodladı. Bu tamsayı dizilerinden 18'i vardı ve bazılarında 30'dan fazla eleman vardı.

Dile bazı ek veri türlerini tanıtmak için MS / SQL için bir ihtiyaç olduğunu düşünüyorum. Diziler oldukça yaygındır ve bunları depolanmış bir işlemde neden kullanamadığınızı anlamıyorum.


6
SQL Server, tablo değerli parametreler ve değişkenler içerdiğinde dizilere ihtiyaç duymaz.
John Saunders

1
Yani bildiğimiz, sorgunun bir tamsayı listesi (bir dizi tarafından geçildi mi?) Ne anlamıyorum sorgunuz cevaplarda verilen yöntemlerden birini kullanmadan nasıl kullanıyor olduğunu. Biraz daha bağlam sağlayın ve size daha fazla yardımcı olabiliriz.
Möoz

2

Bunu böyle yapamazsınız, ancak sorgunun tamamını bir değişkende saklayarak çalıştırabilirsiniz.

Örneğin:

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

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

Exec (@query)

2
Önceki bir yorumda belirtildiği gibi, bu tür bir çözümü nasıl uyguladığınıza bağlı olarak, @listOfIDs bir kullanıcı tarafından sağlanan bir parametreyse bunun SQL enjeksiyonuna karşı savunmasız olabileceğini unutmayın.
Rogala

2

string_splitDize listesini kullanıyorsanız SQL'de yeni bir işlev var . Ref Bağlantısı STRING_SPLIT (Transact-SQL)

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

bu sorguyu inşu şekilde iletebilirsiniz :

SELECT *
  FROM [dbo].[yourTable]
  WHERE (strval IN (SELECT value FROM STRING_SPLIT(@tags, ',') WHERE RTRIM(value) <> ''))

1
Makul görünüyor ama maalesef sadece SQL Server 2016'dan itibaren.
AnotherFineMess

0

Bunu kullanıyorum :

1-Binanızın komut dosyasında geçici tablo değişkeni bildirin:

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2-Geçici tabloya atayın:

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-Tabloya WHERE deyiminde ihtiyaç duyduğunuzda referans verin:

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.