レプリケートされたSQL Serverデータベースを介してベンダーからレポートにアクセスして作成しています。彼らは私が解決しようとしているいくつかの絶対に狂ったことをしましたが、これは簡単です。
彼らは多くの標準的な列を持つテーブルを持っています。ただし、このテーブルには「データ」という列もあります。この列は従来の「テキスト」データ型であり、キーと値のペアの巨大な(数百の)リストが含まれています。各ペアはCRLFで区切られ、キーと値は等号で区切られます。例:
select myTable.[data] from myTable where tblKey = 123
結果:
Key 1=Value 1
Key 2=Value 2
Key 3=Value 3
...
Key 500=Value 500
その列を使用可能なデータのテーブルに分割する最も効率的な方法を決定しようとしています。最終的な目標は、次のように列/フィールドとして指定されたキー/値とともにテーブルキーを返す方法でテーブルをクエリできるようにすることです。
tblKey | [Key 1] | [Key 3] | [Key 243]
-------|---------|---------|-----------
123 Value 1 Value 3 Value 243
124 Value 1 Value 3 Value 243
125 Value 1 Value 3 Value 243
その列をビューに成形する方法はありますか?関数が特に効率的であるとは想像できませんが、string_splitまたはそのようなものを使用して、そのように構文解析できると確信しています。以前にこのタイプの残虐行為に遭遇し、それを操作可能なデータに操作する良い方法を見つけたことがありますか?
dbfiddle サンプルデータを追加して編集します。
データはベンダーのソースから複製されるため、新しいテーブルを作成できません。ビュー、プロシージャ、関数を作成できます。それは私が達成するためのまともな方法のためのアドバイスを探しているものです。
[〜#〜]更新[〜#〜]
あなた自身の答えに投稿したときに、UDFを使用して特定のキー値を取得できる場合は、これを提案させてください(すべてのキー/値を分割する必要はなく、表を再度読む必要もありません) 、テキスト関数を使用して取得できます。)
_CREATE FUNCTION fnGetKey(@Data text, @Key varchar(20))
RETURNS varchar(100)
AS
BEGIN
RETURN
(
SELECT
SUBSTRING (
@Data,
/* Position of first '=' after key + 1 */
CHARINDEX('=', @Data, PATINDEX('%' + @key + '%', @Data)) + 1,
/* Lenght, Position of first chr(13) after key less previuos value - 1 */
(CHARINDEX(CHAR(13), @Data, PATINDEX('%' + @key + '%', @Data))
-
CHARINDEX('=', @Data, PATINDEX('%' + @key + '%', @Data))) - 1
)
)
END
SELECT
FruitID, Name, Description,
dbo.fnGetKey([Data], 'key 2') as [key 2],
dbo.fnGetKey([Data], 'key 4') as [key 4]
FROM
[Fruit];
_
FruitID |名前|説明|キー2 |キー4 ------:| :-- :---------- | :------ | :------ 1 |バナナ|おいしい|値2 |値4 2 |梨| Rotton |値2 |値4 3 |キウイ|わかりました|値2 |値4
db <> fiddle ---(ここ
元の答え
私が理解できる唯一の解決策は、キー/値を分割し、それをピボットして目的の結果を取得することです。
残念ながら、いくつかの不便があります:
text
列では機能しません。したがって、操作する前にvarchar
にキャストする必要があります。nchar(1)
またはnvarchar(1)
が必要です。エルゴでは、CHAR(3)+CHAR(10)
を1文字に置き換える必要があります。Value
を数値データ型にキャストする必要があります。これは私があなたのサンプルデータを使って得たものです:
_WITH KP AS
(
SELECT FruitID, Name, Description, value as KPair
FROM Fruit
CROSS APPLY STRING_SPLIT(REPLACE(CAST(Data AS varchar(max)), CHAR(13)+CHAR(10), ','), ',') /* STRING_SPLIT only allows nchar(1), varchar(1) */
)
, KP1 AS
(
SELECT
FruitID,
SUBSTRING(KPair, 5, CHARINDEX('=', KPair) - 5) AS [Key],
SUBSTRING(KPair, CHARINDEX('=', KPair) + 7, LEN(KPair) - CHARINDEX('=', KPair) - 6) AS [Value]
FROM
KP
)
SELECT [FruitID], [1],[2],[3],[4],[5]
FROM KP1
PIVOT (MAX([Value]) FOR [Key] IN ([1],[2],[3],[4],[5])) AS PVT;
_
最初のCTEは_Key X=Value Y
_ごとに分割します。 2番目は、この値をカットして、それぞれの[キー]と[値]を取得します。そして、最終的なPIVOTは最終結果を列にまとめます。
FruitID | 1 | 2 | 3 | 4 | 5 ------:| : :-| :-| :-| :- 1 | 1 | 2 | 3 | 4 | 5 2 | 1 | 2 | 3 | 4 | 5 3 | 1 | 2 | 3 | 4 | 5
db <> fiddle ---(ここ
注:[キー1]と[値1]を維持する必要があるか、それとも[キー]&[値]という名前の列として変換する必要があるかはわかりません。
別のアプローチ
サードパーティのデータベースを使用する場合は、通常、可能な場合は同じサーバー/インスタンスに新しいデータベースを追加し、DB所有者との競合を避けるために、それを自分の目的で使用します。
この場合、新しいテーブルを追加し、定期的にプロセスをスローして、新しい値で更新することができます。
すべての列を持つテーブルを使用できます。
_CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL PRIMARY KEY,
[V1] int NULL,
[V2] int NULL,
[V3] int NULL,
[V4] int NULL,
[V5] int NULL
);
_
またはキーと値のペアを含むテーブルで、ピボットを使用して最終結果を取得します。
_CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL,
[Key] int NOT NULL,
[Value] int NOT NULL,
CONSTRAINT [PK_FruitKeys] PRIMARY KEY ([FruitID], [Key])
);
_
ソースデータはJSON
形式からそれほど離れていないようです。
あなたはそれをかなり直接変換し、次にOPENJSON
を使用してリレーショナル出力を生成することができます:
SELECT
F.FruitID,
F.[Name],
OJ.[key 1],
OJ.[key 2],
OJ.[key 3],
OJ.[key 4],
OJ.[key 5],
F.[Description]
FROM dbo.Fruit AS F
CROSS APPLY OPENJSON
(
-- Convert source data to JSON format
'{' +
CHAR(34) +
REPLACE
(
REPLACE
(
CONVERT(varchar(max), F.Data),
'=', CHAR(34) + ':' + CHAR(34)
),
CHAR(13) + CHAR(10),
CHAR(34) + ',' + CHAR(34)
) +
CHAR(34) +
'}'
)
WITH
(
[key 1] varchar(100),
[key 2] varchar(100),
[key 3] varchar(100),
[key 4] varchar(100),
[key 5] varchar(100)
) AS OJ;
出力:
FruitID |名前|キー1 |キー2 |キー3 |キー4 |キー5 |説明 ------:| :-- :------ | :------ | :------ | :------ | :------ | :---------- 1 |バナナ|値1 |値2 |値3 |値4 |値5 |美味しい 2 |梨|値1 |値2 |値3 |値4 |値5 |ロトン 3 |キウイ|値1 |値2 |値3 |値4 |値5 |はい
---(db <> fiddleデモ
McNetsは合理的なアプローチを提供しましたが、すべてのペアの分割は、明らかに必要ですが、かなり時間がかかるプロセスです。テーブル内のすべてのレコードに500以上のキーと値のペアがあるため、目的に合うかどうかはわかりません。影響を受けるテーブルのペアが少なく、行数が少ない場合は、おそらく適切なアプローチです。
私は何百ものキーと値のペアとテーブル自体の何千ものレコードを操作しているので、特定のキーと値を使用するレポートとクエリで必要に応じて使用するユーザー定義関数(以下)を実装することを考えていますペアが必要です(既知です)。
CREATE FUNCTION udfsv_GetFruitDataValue(
@FruitID int,
@DataId varchar(100)
)
RETURNS varchar(100)
AS BEGIN
DECLARE @DataVal varchar(100)
set @DataVal = (
select
replace(replace(split1, @DataId + '=', ''), char(13), '') as DataValue
from Fruit
left outer join (
select
FruitID,
value as split1
from Fruit
cross apply string_split(cast([data] as varchar(max)), char(10))
) line1 on line1.FruitID = Fruit.FruitID
where Fruit.FruitID = @FruitID
and split1 like @DataId + '=%'
)
RETURN @DataVal
END
これにより、すべてのキー/値ではなく、指定されたキー/値を含めるクエリを実行できます。
SELECT
FruitID,
Name,
Description,
udfsv_GetFruitDataValue(FruitID, 'Key 1') as [Key 1],
udfsv_GetFruitDataValue(FruitID, 'Key 4') as [Key 4]
FROM
Fruit
WHERE FruitID = 123