정수 목록을 보유 할 SQL 변수


175

다른 사람의 SQL 보고서를 디버깅하려고하고 기본 보고서 쿼리를 SQL 2012의 쿼리 창에 배치했습니다.

보고서가 요구하는 매개 변수 중 하나는 정수 목록입니다. 이는 다중 선택 드롭 다운 상자를 통해 보고서에서 수행됩니다. 보고서의 기본 쿼리는 다음과 같은 where절 에서이 정수 목록을 사용합니다.

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

디버깅하는 쿼리를 수정하고 싶지 않지만 SQL Server에서 테스트를 위해 이러한 유형의 데이터를 보유 할 수있는 변수를 만드는 방법을 알 수 없습니다.

예 :

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

정수 목록을 보유 할 수있는 데이터 유형이 없으므로 보고서와 동일한 값으로 SQL Server에서 보고서 쿼리를 어떻게 실행할 수 있습니까?


1
TVP Table Value Parmeter를 사용하여 데이터를 삽입했지만 이제 어디에서나 사용할 수 있는지 확인했습니다. 계속?
paparazzo

2
잘 표현 된 질문. +1
RayLoveless

답변:


224

테이블 변수

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

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

또는

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
고마워하지만 다시 변수를 쿼리에서 읽는 방식을 다시 작성해야합니다. 나는 그것을 동일하게 유지해야합니다.
ErickTreetops

3
ID가 무엇이며 쿼리에서 오는 ID를 모르는 경우 어떻게해야합니까? 예 : SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)이 쿼리는 여러 ID를 반환하며 하위 쿼리가 둘 이상의 결과를 반환했지만 허용되지 않는다는 오류가 발생합니다. 하위 쿼리의 ID 인 경우 배열을 저장할 변수를 만들 수 있습니까?
Rafael Moreira

1
나는 두 번째 옵션을 시도했으며 적은 수의 레코드에서 작동합니다. ID 수를 늘리면 TimeOut 오류가 발생합니다. 캐스트가 성능을 방해 할 수 있습니다.
사용자 M

원래 질문에 대한 대답은 아니지만 내가 묻지 않은 질문에 대한 대답이므로 좋습니다. List <int>를 매개 변수로 전달했지만 SSMS에서 테스트하기 위해 로컬 변수를 만들고 싶습니다. 살인자 야
웨이드 해 틀러

훌륭한 답변, 많은 시간을 절약했습니다
Alfredo A.

35

변수를 가정하면 다음과 유사합니다.

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

저장 프로시 저는 다음 형식으로 사용합니다.

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

IntList를 작성하고 다음과 같이 프로 시저를 호출 할 수 있습니다.

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

또는 IntList를 직접 제공하는 경우

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

17

맞습니다. SQL Server에는 정수 목록을 보유 할 수있는 데이터 유형이 없습니다. 그러나 할 수있는 일은 정수 목록을 문자열로 저장하는 것입니다.

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

그런 다음 문자열을 별도의 정수 값으로 분할하여 테이블에 넣을 수 있습니다. 절차가 이미이 작업을 수행했을 수 있습니다.

동적 쿼리를 사용하여 동일한 결과를 얻을 수도 있습니다.

DECLARE @SQL nvarchar(8000);

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

고마워하지만 다시는 허용되지 않는 쿼리를 수정해야합니다.
ErickTreetops

2
누군가 이것을 사용하는 경우 @listOfIDs가 사용자가 제공 한 문자열 매개 변수 인 경우 SQL 주입에 매우 취약 할 수 있습니다. 앱의 아키텍처에 따라 문제가 될 수도 있고 아닐 수도 있습니다.
Rogala

@Rogala Agreed 사용자는 필요에 따라 자체 위생을 수행해야합니다.
Möoz

1
@ Möoz 답변에 메모를 추가하여이를 반영하는 것이 좋습니다. 모두가 아는 것은 아니며 결과에 대해 생각하지 않고 솔루션을 복사하여 붙여 넣기 만하면됩니다. 동적 SQL은 매우 위험하므로 전염병처럼 피하십시오.
Rogala

@ Möoz 또한, 귀하의 답변에 투표하지 않았지만 몇 분 동안 답변을 확인하십시오. STRING_SPLIT 함수는 꽤 달콤합니다.
Rogala

6

SQL Server 2016+ 및 Azure SQL Database의 경우 STRING_SPLIT 함수가 추가되어이 문제에 대한 완벽한 솔루션이되었습니다. 다음은 설명서입니다. https://docs.microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

예를 들면 다음과 같습니다.

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

쿼리 결과는 1,3입니다.

~ 건배


4

결국 쿼리가 작동하는 방식을 수정하지 않고 값을 변수에 저장할 수 없다는 결론에 도달했습니다. SQL 프로파일 러를 사용하여 값을 포착 한 다음 쿼리에 하드 코딩하여 작동 방식을 확인했습니다. 이 정수 배열 중 18 개가 있고 일부에는 30 개가 넘는 요소가있었습니다.

MS / SQL이 일부 추가 데이터 유형을 언어에 도입 할 필요가 있다고 생각합니다. 배열은 매우 일반적이며 왜 저장된 proc에서 사용할 수 없는지 알 수 없습니다.


6
SQL Server에는 테이블 반환 매개 변수 및 변수가있을 때 배열이 필요하지 않습니다.
존 손더스

1
우리가 아는 것은 쿼리가 정수 목록을 사용한다는 것입니다 (배열에 의해 전달 되었습니까?). 내가 이해하지 못하는 것은 답변에 주어진 방법 중 하나를 사용하지 않고 쿼리가 쿼리를 어떻게 사용했는지입니다. 더 많은 맥락을 제공하면 더 도움을 줄 수 있습니다.
Möoz

2

이런 식으로 할 수는 없지만 변수에 저장하는 전체 쿼리를 실행할 수 있습니다.

예를 들면 다음과 같습니다.

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

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

Exec (@query)

2
이전 주석에서 언급했듯이이 유형의 솔루션을 구현하는 방법에 따라 @listOfIDs가 사용자가 제공 한 매개 변수 인 경우 SQL 삽입에 취약 할 수 있습니다.
Rogala

2

string_split문자열 목록을 사용하는 경우 SQL에 새로운 함수가 호출되었습니다 . 참조 링크 STRING_SPLIT (Transact-SQL)

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

in다음과 같이이 쿼리를 전달할 수 있습니다 .

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

1
불행하게도 그럴듯하지만 SQL Server 2016 이상 만 보입니다.
AnotherFineMess

0

나는 이것을 사용한다 :

1-건물 스크립트에서 임시 테이블 변수를 선언하십시오.

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2- 임시 테이블에 할당 :

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

WHERE 문에서 필요할 때 표를 3 참조하십시오.

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.