2つの列(整数IDとテキストベースの文字列)のテーブルがある場合、中かっこで囲まれた任意の数の整数をエンコードし、他の有効なテキスト文字と混合した文字列値から始めたいと思います。
例:'{1} / {9} ... {12}'
単一のSELECT
ステートメントを使用して、すべての整数(およびその中括弧)がテーブルから派生した値に置き換えられた文字列を返したいと思います。具体的には、ソース文字列で見つかった番号と一致するIDを持つ行のテキスト値....と中括弧の外の文字は変更されません。
タスクを完了できない例を次に示します。
select
replace('{13} {15}','{'+cast(id as varchar)+'}',isNull(display,''))
from testing;
これにより、testing
テーブルの行ごとに1行が返されます。 id
値= 13の行の場合、文字列の '{13}'部分は正常に置き換えられますが、 '{15}'部分は置き換えられません(行15ではその逆です)。
すべてのtesting
行をループして繰り返し置換を試行する関数を作成すると、問題が解決すると思います。いずれにせよ、ループよりもまっすぐなSQLステートメントの方が望ましいでしょう。
サンプルデータ
+----+-------------------+
| id | display |
+----+-------------------+
| 1 | Apple |
| 2 | Banana |
| 3 | Celery |
| 4 | Dragonfruit |
| 5 | Eggplant |
| 6 | Fenugreek |
| 7 | Gourd |
| 8 | Honeydew |
| 9 | Iceberg Lettuce |
| 10 | Jackfruit |
| 11 | Kale |
| 12 | Lemon |
| 13 | Mandarin |
| 14 | Nectarine |
| 15 | Olive |
+----+-------------------+
使用例
select replace('{1} {3}',null,null)
-- Returns 'Apple Celery'
select replace('{3},{4},{5}',null,null);
-- Returns 'Celery,Dragonfruit,Eggplant'
select replace('{1} / {9} ... {12}',null,null);
-- Returns 'Apple / Iceberg Lettuce ... Lemon'
明らかに、replace
キーワードは機能しません。
PS。これを容易にするためにソリューションが文字列のフォーマットを変更する必要がある場合、それはオプションです。
例:'#1 / #9 ... #12'
(前の例と関連付けるため)
このフォーマットでは、#
に基づいて文字列を行セットに分割し、取得した数値に基づいてleft
からjoin
テーブルへの非数値を見つけるまでtesting
文字を取得し、#
と、testing
テーブルのdisplay
値を持つ数値、次にstuff
これらの個別に変更されたすべてのトークンを単一の文字列に戻しますfor xml path
?
string_agg
をサポートしないSQL Server 2016を使用しています。とはいえ、string_agg
を使用した解決策がある場合は、引き続き検討することにします。
これは再帰的なcte
を使用した例ですtranslate変数
drop table if exists testing;
go
create table testing (id int, display varchar(16));
insert into testing values (1, 'Apple');
insert into testing values (2, 'Banana');
insert into testing values (3, 'Celery');
insert into testing values (4, 'Dragonfruit');
insert into testing values (5, 'Eggplant');
DROP FUNCTION IF EXISTS dbo.TranslateVariables
go
CREATE FUNCTION dbo.TranslateVariables
(
@StringValue VARCHAR(MAX)
)
RETURNS TABLE
AS
RETURN (
--Common Table Expression for Translation
WITH TranslationTable
AS (
SELECT FindValue = '{' + convert(varchar(5),id) + '}' ,ReplaceValue = display,ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
FROM testing
)
--Recursive CTE to loop through the TranslationTable and replace FindValue with ReplaceValue
,RecursiveCte as
(
SELECT @StringValue AS StrValue
,(
SELECT count(*)
FROM TranslationTable
) AS cnt
UNION ALL
SELECT replace(StrValue, tt.FindValue, tt.Replacevalue)
,cnt - 1
FROM RecursiveCte
JOIN TranslationTable tt
ON tt.rn = cnt )
SELECT StrValue
,cnt
FROM RecursiveCte where cnt = 0
)
go
--Verify translation
SELECT *
FROM dbo.TranslateVariables('{1} {3}')
OPTION (MAXRECURSION 32767) -- Don't forget to use the maxrecursion option!
StrValue | cnt |
|--------------|-----|
| Apple Celery | 0 |
SELECT *
FROM dbo.TranslateVariables('{3},{4},{5}')
OPTION (MAXRECURSION 32767) -- Don't forget to use the maxrecursion option!
| StrValue | cnt |
|-----------------------------|-----|
| Celery,Dragonfruit,Eggplant | 0 |
あなたがそれをサポートするバージョンを使用していると仮定し(そして 元のフィドル でバージョンを終了する)、ネイティブ string_split()
&-を使用できます string_agg()
関数。
_declare @id_list varchar(10) = '1,3'; -- for 'Apple,Celery'
-- set @id_list = '3,4,5'; --for 'Celery,Dragonfruit,Eggplant'
select string_agg(display, ',') as agg
from (
select t.display
from testing t
cross apply string_split(@id_list,',') ss
where try_cast(ss.[value] as int) = t.id
) x;
_
上記の例では、途中で中かっこを削除し、コンマで区切られた数値のリストであると想定しています。中かっこを途中で保持したい場合は、整形式のJSONが含まれていることを確認し、解析に ネイティブJSON関数 を使用する必要があります。上記の主要なビットは次のとおりです。
[Id]
_ sの配列を提供...testing.display
_ sのみに絞り込み、次に...string_agg()
にフィードしますあなたが2016年であり、string_agg()
が2017年にのみ利用可能になることを明記すると、string_split()
を使用して必要に応じて配列を作成し、従来のアプローチの1つを使用できますOPで検討しているように、string_agg()
がないことを回避します。例えば:
_select stuff(agg,1,1,'') as agg_trim_first_comma
from (
select stuff(x.display,1,0,'')
from (
select ',' + t.display
from testing t
cross apply string_split('1,3',',') ss
where try_cast(ss.[value] as int) = t.id
) x (display )
for xml path('')
) y (agg);
_