web-dev-qa-db-fra.com

Variable SQL pour contenir la liste des entiers

J'essaie de déboguer les rapports SQL de quelqu'un d'autre et j'ai placé la requête de rapports sous-jacente dans une fenêtre de requête de SQL 2012.

L'un des paramètres que le rapport demande est une liste d'entiers. Ceci est réalisé sur le rapport via une liste déroulante à sélections multiples. La requête sous-jacente du rapport utilise cette liste d'entiers dans la clause where, par exemple.

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

Je ne souhaite pas modifier la requête en cours de débogage, mais je ne vois pas comment créer une variable sur le serveur SQL pouvant contenir ce type de données pour la tester.

par exemple.

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

Aucun type de données pouvant contenir une liste d'entiers. Comment puis-je exécuter la requête de rapport sur mon serveur SQL Server avec les mêmes valeurs que le rapport?

138
user1413844

variable de table

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

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

ou

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
187
slavoo

En supposant que la variable ressemble à quelque chose comme:

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

Et la procédure stockée l'utilise sous cette forme:

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

Vous pouvez créer la liste IntList et appeler la procédure comme suit:

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

Ou si vous fournissez vous-même IntList

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

Vous avez raison, il n’existe pas de type de données dans SQL-Server pouvant contenir une liste d’entiers. Mais ce que vous pouvez faire est de stocker une liste d’entiers sous forme de chaîne.

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

Vous pouvez ensuite scinder la chaîne en valeurs entières distinctes et les placer dans un tableau. Votre procédure peut déjà le faire.

Vous pouvez également utiliser une requête dynamique pour obtenir le même résultat:

DECLARE @SQL nvarchar(8000);

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

Pour SQL Server 2016+ et la base de données Azure SQL, la fonction STRING_SPLIT a été ajoutée. Elle constituerait une solution parfaite à ce problème. Voici la documentation: https://docs.Microsoft.com/en-us/sql/t-sql/functions/string-split-transact-sql

Voici un exemple:

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

Le résultat de la requête est 1,3

~ A bientôt

5
Rogala

Finalement, j'ai conclu que, sans modifier le fonctionnement de la requête, je ne pouvais pas stocker les valeurs dans des variables. J'ai utilisé le profileur SQL pour capturer les valeurs, puis les coder en dur dans la requête pour voir comment cela fonctionnait. Il y avait 18 de ces tableaux de nombres entiers et certains avaient plus de 30 éléments.

Je pense qu'il est nécessaire que MS/SQL introduit quelques types de données supplémentaires dans le langage. Les tableaux sont assez courants et je ne vois pas pourquoi vous ne pourriez pas les utiliser dans un proc stocké.

4
user1413844

Vous ne pouvez pas le faire comme ceci, mais vous pouvez exécuter la requête entière en la stockant dans une variable.

Par exemple:

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

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

Exec (@query)
2
thepirat000

Il existe une nouvelle fonction SQL appelée string_split si vous utilisez une liste de chaînes. Lien de référence STRING_SPLIT (Transact-SQL)

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

vous pouvez passer cette requête avec in comme suit:

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

J'utilise ceci:

1-Déclarez une variable de table temporaire dans le script de votre construction:

DECLARE @ShiftPeriodList TABLE(id INT NOT NULL);

2-Allocate to temp table:

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-Référencez la table lorsque vous en avez besoin dans une instruction WHERE:

INSERT INTO SomeTable WHERE ShiftPeriod IN (SELECT * FROM @ShiftPeriodList)
0