私は他の誰かのSQLレポートをデバッグしようとしていて、基礎となるレポートクエリをSQL 2012のクエリウィンドウに配置しました。
レポートが要求するパラメータの1つは整数のリストです。これは、複数選択ドロップダウンボックスを介してレポートに表示されます。レポートの基になるクエリはwhere
節でこの整数リストを使用します。
select *
from TabA
where TabA.ID in (@listOfIDs)
デバッグしているクエリを変更したくはありませんが、テストするためにこの種のデータを保持できるSQL Server上の変数を作成する方法を理解することはできません。
例えば.
declare @listOfIDs int
set listOfIDs = 1,2,3,4
整数のリストを保持できるデータ型はありません。そのため、SQL Serverでレポートと同じ値を使用してレポートクエリを実行するにはどうすればよいですか。
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
変数が次のようなものであると仮定します。
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);
そうです、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);
SQL Server 2016以降およびAzure SQLデータベースでは、この問題を解決するのに最適なSTRING_SPLIT関数が追加されました。これがドキュメントです。 https://docs.Microsoft.com/ja-jp/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です。
〜乾杯
結局、クエリの動作方法を変更しなければ、変数に値を格納できないという結論に至りました。私はSQLプロファイラーを使用して値をキャッチし、それからそれらがどのように機能するのかを確認するためにクエリーにハードコーディングしました。これらの整数配列は18個あり、その中に30個を超える要素を持つものもありました。
MS/SQLがいくつかの追加のデータ型を言語に導入する必要があると思います。配列はごく一般的なので、ストアドプロシージャで使用できない理由がわかりません。
これはできませんが、変数に格納したクエリ全体を実行することはできます。
例えば:
DECLARE @listOfIDs NVARCHAR(MAX) =
'1,2,3'
DECLARE @query NVARCHAR(MAX) =
'Select *
From TabA
Where TabA.ID in (' + @listOfIDs + ')'
Exec (@query)
文字列のリストを使用している場合は、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-建物のスクリプトで一時テーブル変数を宣言します。
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)