Змінна SQL для вміщення списку цілих чисел


175

Я намагаюся налагоджувати чужі звіти SQL і розміщував базовий запит звітів у вікнах запитів SQL 2012.

Одним із параметрів, про який запитує звіт, є список цілих чисел. Це досягається у звіті за допомогою випадаючого вікна з декількома виборами. Основний запит звіту використовує цей цілий список у whereпункті, наприклад,

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

Я не хочу змінювати запит, який я налагоджую, але не можу зрозуміти, як створити змінну на SQL сервері, яка може містити дані цього типу для тестування.

напр

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

Немає типу даних, який може містити список цілих чисел, тож як я можу запустити запит звіту на моєму SQL Server із тими ж значеннями, що і звіт?


1
Я знаю, що для вставки даних я використовував параметр таблиці таблиць TVP, але тепер впевнений, чи можна його використовувати де. Сиквел?
папараццо

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
Що робити, якщо ви не знаєте, що таке ідентифікатори, а це походить від запиту? Приклад: SET @AddressIDs = (SELECT ID FROM address WHERE Account = 1234)Цей запит повертає декілька ідентифікаторів, і я отримую помилку про те, що підзапит повертав більше одного результату, і це заборонено. Чи варто створити змінну, яка зберігатиме масив, якщо ідентифікатори з підзапиту?
Рафаель Морейра

1
Я спробував другий варіант, і він працює для меншої кількості записів. Коли я збільшую кількість ідентифікаторів, я отримую помилку TimeOut. Можливо, акторський склад перешкоджає виступу.
Користувач M

Не відповідь на оригінальне запитання, але це відповідь на одне, якого я не запитував, тому я хороший. Я передаю список <int> як параметр, але хочу зробити локальну змінну для тестування в SSMS. Це вбивця.
Вейд Хатлер

Чудова відповідь, врятував мені багато часу
Альфредо А.

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-сервері немає типу даних, який міг би містити список цілих чисел. Але те, що ви можете зробити, це зберегти список цілих чисел як рядок.

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
Якщо хтось цим користується, майте на увазі, що це може бути дуже вразливим до ін'єкції SQL, якщо @listOfIDs є рядковим параметром, наданим користувачем. Залежно від архітектури вашого додатка, це може бути чи не може бути проблемою.
Rogala

@ Рогала Погодився, користувачі повинні робити власні санітарні умови за потребою.
Möoz

1
@ Möoz я рекомендую додати відповідь до своєї відповіді, щоб відобразити це. Не всі знають, і вони просто копіюють і вставляють рішення, не замислюючись про наслідки. Динамічний SQL ДУЖЕ небезпечний, і я уникаю його як чуму.
Рогала

@ Möoz Крім того, я не відмовився голосувати за вашу відповідь, але що ви, будь ласка, знайдіть кілька хвилин і перевірити мою відповідь? Функція STRING_SPLIT дуже приємна, і я думаю, ви будете ВСІ над цим !!
Рогала

6

Для SQL Server 2016+ та бази даних Azure SQL додана функція 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 потребує введення деяких додаткових типів даних у мову. Масиви є досить поширеними, і я не бачу, чому ви не могли використовувати їх у збереженій програмі.


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
Як зазначено в попередньому коментарі, залежно від того, як ви реалізуєте цей тип рішення, пам’ятайте, що це може бути вразливим до ін'єкції SQL, якщо @listOfIDs є параметром, наданим користувачем.
Рогала

2

У SQL є нова функція, яка називається, string_splitякщо ви використовуєте список рядків. Посилання посилання 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

3 - Посилайтеся на таблицю, коли вона вам потрібна, у викладі WHERE:

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.