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?
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
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);
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);
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
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é.
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)
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) <> ''))
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)