Manipulating a Column Containing Key/Value PairsSearch on part of a column in a SQL Server cross-database viewSQL Calculation based on 'similar' rows within tableupdating the entire column with datetime values based on other values in the rowSudden PRIMARY KEY violation on IDENTITY columnHow to optimise multiple joins with OR?Easy way to change Column ID (primary key) of a table to Auto incrementEfficient way to handle multiple CASE statements in SELECTSet up View Column to be last Valuejoin two tables, include one row from the parent table for each primary keyAdd Constraint to check value exists in a non-unique composite key column
Why such a singular place for bird watching?
Isn't the detector always measuring, and thus always collapsing the state?
Why aren't faces sharp in my f/1.8 portraits even though I'm carefully using center-point autofocus?
Enlightenment finding me
Missing quartile in boxplot
Probability of going broke
How important is knowledge of trig identities for use in Calculus
How to say "respectively" in German when listing (enumerating) things
Can Familiars read and use spell scrolls?
Principled construction of the quaternions
Would an object shot from earth fall into the sun?
How do we decide/plan an SLA for an NP-hard optimization process running in production?
Is "weekend warrior" derogatory?
Airport Security - advanced check, 4th amendment breach
How do we know neutrons have no charge?
Is there an in-universe explanation of how Frodo's arrival in Valinor was recorded in the Red Book?
Giving a good fancy look to a simple table
What is the use of command?
Are there types of animals that can't make the trip to space? (physiologically)
How dangerous are my worn rims?
Can UK supreme court justices be evaluated ideologically?
How to have hashes doubled _and_ things expanded?
Knights and Knaves: What does C say?
Does the US Armed Forces refuse to recruit anyone with an IQ less than 83?
Manipulating a Column Containing Key/Value Pairs
Search on part of a column in a SQL Server cross-database viewSQL Calculation based on 'similar' rows within tableupdating the entire column with datetime values based on other values in the rowSudden PRIMARY KEY violation on IDENTITY columnHow to optimise multiple joins with OR?Easy way to change Column ID (primary key) of a table to Auto incrementEfficient way to handle multiple CASE statements in SELECTSet up View Column to be last Valuejoin two tables, include one row from the parent table for each primary keyAdd Constraint to check value exists in a non-unique composite key column
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty
margin-bottom:0;
I'm accessing and creating reports from a vendor via a replicated SQL Server database. They've done some absolutely insane things that I've been trying to solve for, but this one takes the cake.
They have a table that has many standard columns. But this table also has a column called "Data". The column is a legacy "text" data type, and it contains a giant (hundreds) list of key/value pairs. Each pair is separated by a CRLF, and the key and value are separated by an equal sign. Example:
select myTable.[data] from myTable where tblKey = 123
Result:
Key 1=Value 1
Key 2=Value 2
Key 3=Value 3
...
Key 500=Value 500
I'm trying to determine the most efficient way to break that column out into a usable table of data. The end goal would be to be able to query the table in a way that returns the table key along with specified key/values as column/fields as such:
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
Is there a way to mold that column into a View? I can't imagine that a Function would be particularly efficient, but I'm sure I could parse things out that way using a string_split or something of that sort. Has anyone run into this type of atrocity before and found a good way to manipulate it into usable data?
Edit to add dbfiddle sample data.
The data is replicated from a vendor's source, so I can't create new tables. I can create views, procedures and functions. That's what I'm looking for advice for a decent way to accomplish.
sql-server sql-server-2016 join view enterprise-edition
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment
|
I'm accessing and creating reports from a vendor via a replicated SQL Server database. They've done some absolutely insane things that I've been trying to solve for, but this one takes the cake.
They have a table that has many standard columns. But this table also has a column called "Data". The column is a legacy "text" data type, and it contains a giant (hundreds) list of key/value pairs. Each pair is separated by a CRLF, and the key and value are separated by an equal sign. Example:
select myTable.[data] from myTable where tblKey = 123
Result:
Key 1=Value 1
Key 2=Value 2
Key 3=Value 3
...
Key 500=Value 500
I'm trying to determine the most efficient way to break that column out into a usable table of data. The end goal would be to be able to query the table in a way that returns the table key along with specified key/values as column/fields as such:
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
Is there a way to mold that column into a View? I can't imagine that a Function would be particularly efficient, but I'm sure I could parse things out that way using a string_split or something of that sort. Has anyone run into this type of atrocity before and found a good way to manipulate it into usable data?
Edit to add dbfiddle sample data.
The data is replicated from a vendor's source, so I can't create new tables. I can create views, procedures and functions. That's what I'm looking for advice for a decent way to accomplish.
sql-server sql-server-2016 join view enterprise-edition
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
There are currently 517 keys, but that number occasionally grows. The name of the database is not emdb.
– Andy
7 hours ago
add a comment
|
I'm accessing and creating reports from a vendor via a replicated SQL Server database. They've done some absolutely insane things that I've been trying to solve for, but this one takes the cake.
They have a table that has many standard columns. But this table also has a column called "Data". The column is a legacy "text" data type, and it contains a giant (hundreds) list of key/value pairs. Each pair is separated by a CRLF, and the key and value are separated by an equal sign. Example:
select myTable.[data] from myTable where tblKey = 123
Result:
Key 1=Value 1
Key 2=Value 2
Key 3=Value 3
...
Key 500=Value 500
I'm trying to determine the most efficient way to break that column out into a usable table of data. The end goal would be to be able to query the table in a way that returns the table key along with specified key/values as column/fields as such:
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
Is there a way to mold that column into a View? I can't imagine that a Function would be particularly efficient, but I'm sure I could parse things out that way using a string_split or something of that sort. Has anyone run into this type of atrocity before and found a good way to manipulate it into usable data?
Edit to add dbfiddle sample data.
The data is replicated from a vendor's source, so I can't create new tables. I can create views, procedures and functions. That's what I'm looking for advice for a decent way to accomplish.
sql-server sql-server-2016 join view enterprise-edition
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
I'm accessing and creating reports from a vendor via a replicated SQL Server database. They've done some absolutely insane things that I've been trying to solve for, but this one takes the cake.
They have a table that has many standard columns. But this table also has a column called "Data". The column is a legacy "text" data type, and it contains a giant (hundreds) list of key/value pairs. Each pair is separated by a CRLF, and the key and value are separated by an equal sign. Example:
select myTable.[data] from myTable where tblKey = 123
Result:
Key 1=Value 1
Key 2=Value 2
Key 3=Value 3
...
Key 500=Value 500
I'm trying to determine the most efficient way to break that column out into a usable table of data. The end goal would be to be able to query the table in a way that returns the table key along with specified key/values as column/fields as such:
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
Is there a way to mold that column into a View? I can't imagine that a Function would be particularly efficient, but I'm sure I could parse things out that way using a string_split or something of that sort. Has anyone run into this type of atrocity before and found a good way to manipulate it into usable data?
Edit to add dbfiddle sample data.
The data is replicated from a vendor's source, so I can't create new tables. I can create views, procedures and functions. That's what I'm looking for advice for a decent way to accomplish.
sql-server sql-server-2016 join view enterprise-edition
sql-server sql-server-2016 join view enterprise-edition
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
edited 8 hours ago
Andy
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
asked 8 hours ago
AndyAndy
213 bronze badges
213 bronze badges
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
There are currently 517 keys, but that number occasionally grows. The name of the database is not emdb.
– Andy
7 hours ago
add a comment
|
There are currently 517 keys, but that number occasionally grows. The name of the database is not emdb.
– Andy
7 hours ago
There are currently 517 keys, but that number occasionally grows. The name of the database is not emdb.
– Andy
7 hours ago
There are currently 517 keys, but that number occasionally grows. The name of the database is not emdb.
– Andy
7 hours ago
add a comment
|
3 Answers
3
active
oldest
votes
It seems to me that the source data is not that far from JSON format.
You could convert it pretty directly then use OPENJSON to produce a relational output:
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;
Output:
FruitID | Name | key 1 | key 2 | key 3 | key 4 | key 5 | Description
------: | :----- | :------ | :------ | :------ | :------ | :------ | :----------
1 | Banana | value 1 | value 2 | value 3 | value 4 | value 5 | Delicious
2 | Pear | value 1 | value 2 | value 3 | value 4 | value 5 | Rotton
3 | Kiwi | value 1 | value 2 | value 3 | value 4 | value 5 | Okay
db<>fiddle demo
add a comment
|
The only solution I can figure out is by splitting key/values and then pivot it to obtain the desired result.
Unfortunately there are some inconveniences:
- STRING_SPLIT doesn't works with
textcolumns. Hence you must cast it tovarcharbefore you are able to manipulate it. - STRING_SPLIT requires a
nchar(1)ornvarchar(1), ergo you should replaceCHAR(3)+CHAR(10)by a single character. - Aggregate function on PIVOT works better with numeric values, then you should cast
Valueto some numeric data type. - PIVOT needs a well-known number of columns, in my example I have used a few of them but you should write the whole sequence unless you'd rather deal with dynamic queries.
This is what I've got using your sample data:
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;
First CTE split every Key X=Value Y. The second one cut this value to obtain each [Key] and [Value]. And the final PIVOT compose the final result in columns.
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 here
NOTE: I'm not sure if I should maintain [Key 1] & [Value 1] or it should be converted as a column named [Key] & [Value].
A different approach
When I work with 3rd party databases I usually add a new database, on the same server/instance if possible, and then I use it for my own purposes, just to avoid conflicts with the DB owners.
In this case you could add a new table and periodically throw a process to update it with the new values.
You could use a table with all columns:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL PRIMARY KEY,
[V1] int NULL,
[V2] int NULL,
[V3] int NULL,
[V4] int NULL,
[V5] int NULL
);
or a table with Key/Value pairs and use a pivot to obtain the final result:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL,
[Key] int NOT NULL,
[Value] int NOT NULL,
CONSTRAINT [PK_FruitKeys] PRIMARY KEY ([FruitID], [Key])
);
This is an effective approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
– Andy
3 hours ago
add a comment
|
McNets provided a reasonable approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
Because I'm working with hundreds of key/value pairs and also thousands of records in the table itself, I'm thinking about implementing a user defined function (below) to use as-needed in reports and queries where a specific key/value pair is needed (and known).
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
With this I'd be able to perform queries to include specified key/values, just not all key/values.
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
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment
|
Your Answer
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "182"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/4.0/"u003ecc by-sa 4.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Andy is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f249593%2fmanipulating-a-column-containing-key-value-pairs%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
It seems to me that the source data is not that far from JSON format.
You could convert it pretty directly then use OPENJSON to produce a relational output:
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;
Output:
FruitID | Name | key 1 | key 2 | key 3 | key 4 | key 5 | Description
------: | :----- | :------ | :------ | :------ | :------ | :------ | :----------
1 | Banana | value 1 | value 2 | value 3 | value 4 | value 5 | Delicious
2 | Pear | value 1 | value 2 | value 3 | value 4 | value 5 | Rotton
3 | Kiwi | value 1 | value 2 | value 3 | value 4 | value 5 | Okay
db<>fiddle demo
add a comment
|
It seems to me that the source data is not that far from JSON format.
You could convert it pretty directly then use OPENJSON to produce a relational output:
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;
Output:
FruitID | Name | key 1 | key 2 | key 3 | key 4 | key 5 | Description
------: | :----- | :------ | :------ | :------ | :------ | :------ | :----------
1 | Banana | value 1 | value 2 | value 3 | value 4 | value 5 | Delicious
2 | Pear | value 1 | value 2 | value 3 | value 4 | value 5 | Rotton
3 | Kiwi | value 1 | value 2 | value 3 | value 4 | value 5 | Okay
db<>fiddle demo
add a comment
|
It seems to me that the source data is not that far from JSON format.
You could convert it pretty directly then use OPENJSON to produce a relational output:
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;
Output:
FruitID | Name | key 1 | key 2 | key 3 | key 4 | key 5 | Description
------: | :----- | :------ | :------ | :------ | :------ | :------ | :----------
1 | Banana | value 1 | value 2 | value 3 | value 4 | value 5 | Delicious
2 | Pear | value 1 | value 2 | value 3 | value 4 | value 5 | Rotton
3 | Kiwi | value 1 | value 2 | value 3 | value 4 | value 5 | Okay
db<>fiddle demo
It seems to me that the source data is not that far from JSON format.
You could convert it pretty directly then use OPENJSON to produce a relational output:
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;
Output:
FruitID | Name | key 1 | key 2 | key 3 | key 4 | key 5 | Description
------: | :----- | :------ | :------ | :------ | :------ | :------ | :----------
1 | Banana | value 1 | value 2 | value 3 | value 4 | value 5 | Delicious
2 | Pear | value 1 | value 2 | value 3 | value 4 | value 5 | Rotton
3 | Kiwi | value 1 | value 2 | value 3 | value 4 | value 5 | Okay
db<>fiddle demo
answered 2 hours ago
Paul White♦Paul White
59.8k16 gold badges310 silver badges489 bronze badges
59.8k16 gold badges310 silver badges489 bronze badges
add a comment
|
add a comment
|
The only solution I can figure out is by splitting key/values and then pivot it to obtain the desired result.
Unfortunately there are some inconveniences:
- STRING_SPLIT doesn't works with
textcolumns. Hence you must cast it tovarcharbefore you are able to manipulate it. - STRING_SPLIT requires a
nchar(1)ornvarchar(1), ergo you should replaceCHAR(3)+CHAR(10)by a single character. - Aggregate function on PIVOT works better with numeric values, then you should cast
Valueto some numeric data type. - PIVOT needs a well-known number of columns, in my example I have used a few of them but you should write the whole sequence unless you'd rather deal with dynamic queries.
This is what I've got using your sample data:
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;
First CTE split every Key X=Value Y. The second one cut this value to obtain each [Key] and [Value]. And the final PIVOT compose the final result in columns.
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 here
NOTE: I'm not sure if I should maintain [Key 1] & [Value 1] or it should be converted as a column named [Key] & [Value].
A different approach
When I work with 3rd party databases I usually add a new database, on the same server/instance if possible, and then I use it for my own purposes, just to avoid conflicts with the DB owners.
In this case you could add a new table and periodically throw a process to update it with the new values.
You could use a table with all columns:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL PRIMARY KEY,
[V1] int NULL,
[V2] int NULL,
[V3] int NULL,
[V4] int NULL,
[V5] int NULL
);
or a table with Key/Value pairs and use a pivot to obtain the final result:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL,
[Key] int NOT NULL,
[Value] int NOT NULL,
CONSTRAINT [PK_FruitKeys] PRIMARY KEY ([FruitID], [Key])
);
This is an effective approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
– Andy
3 hours ago
add a comment
|
The only solution I can figure out is by splitting key/values and then pivot it to obtain the desired result.
Unfortunately there are some inconveniences:
- STRING_SPLIT doesn't works with
textcolumns. Hence you must cast it tovarcharbefore you are able to manipulate it. - STRING_SPLIT requires a
nchar(1)ornvarchar(1), ergo you should replaceCHAR(3)+CHAR(10)by a single character. - Aggregate function on PIVOT works better with numeric values, then you should cast
Valueto some numeric data type. - PIVOT needs a well-known number of columns, in my example I have used a few of them but you should write the whole sequence unless you'd rather deal with dynamic queries.
This is what I've got using your sample data:
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;
First CTE split every Key X=Value Y. The second one cut this value to obtain each [Key] and [Value]. And the final PIVOT compose the final result in columns.
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 here
NOTE: I'm not sure if I should maintain [Key 1] & [Value 1] or it should be converted as a column named [Key] & [Value].
A different approach
When I work with 3rd party databases I usually add a new database, on the same server/instance if possible, and then I use it for my own purposes, just to avoid conflicts with the DB owners.
In this case you could add a new table and periodically throw a process to update it with the new values.
You could use a table with all columns:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL PRIMARY KEY,
[V1] int NULL,
[V2] int NULL,
[V3] int NULL,
[V4] int NULL,
[V5] int NULL
);
or a table with Key/Value pairs and use a pivot to obtain the final result:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL,
[Key] int NOT NULL,
[Value] int NOT NULL,
CONSTRAINT [PK_FruitKeys] PRIMARY KEY ([FruitID], [Key])
);
This is an effective approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
– Andy
3 hours ago
add a comment
|
The only solution I can figure out is by splitting key/values and then pivot it to obtain the desired result.
Unfortunately there are some inconveniences:
- STRING_SPLIT doesn't works with
textcolumns. Hence you must cast it tovarcharbefore you are able to manipulate it. - STRING_SPLIT requires a
nchar(1)ornvarchar(1), ergo you should replaceCHAR(3)+CHAR(10)by a single character. - Aggregate function on PIVOT works better with numeric values, then you should cast
Valueto some numeric data type. - PIVOT needs a well-known number of columns, in my example I have used a few of them but you should write the whole sequence unless you'd rather deal with dynamic queries.
This is what I've got using your sample data:
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;
First CTE split every Key X=Value Y. The second one cut this value to obtain each [Key] and [Value]. And the final PIVOT compose the final result in columns.
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 here
NOTE: I'm not sure if I should maintain [Key 1] & [Value 1] or it should be converted as a column named [Key] & [Value].
A different approach
When I work with 3rd party databases I usually add a new database, on the same server/instance if possible, and then I use it for my own purposes, just to avoid conflicts with the DB owners.
In this case you could add a new table and periodically throw a process to update it with the new values.
You could use a table with all columns:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL PRIMARY KEY,
[V1] int NULL,
[V2] int NULL,
[V3] int NULL,
[V4] int NULL,
[V5] int NULL
);
or a table with Key/Value pairs and use a pivot to obtain the final result:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL,
[Key] int NOT NULL,
[Value] int NOT NULL,
CONSTRAINT [PK_FruitKeys] PRIMARY KEY ([FruitID], [Key])
);
The only solution I can figure out is by splitting key/values and then pivot it to obtain the desired result.
Unfortunately there are some inconveniences:
- STRING_SPLIT doesn't works with
textcolumns. Hence you must cast it tovarcharbefore you are able to manipulate it. - STRING_SPLIT requires a
nchar(1)ornvarchar(1), ergo you should replaceCHAR(3)+CHAR(10)by a single character. - Aggregate function on PIVOT works better with numeric values, then you should cast
Valueto some numeric data type. - PIVOT needs a well-known number of columns, in my example I have used a few of them but you should write the whole sequence unless you'd rather deal with dynamic queries.
This is what I've got using your sample data:
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;
First CTE split every Key X=Value Y. The second one cut this value to obtain each [Key] and [Value]. And the final PIVOT compose the final result in columns.
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 here
NOTE: I'm not sure if I should maintain [Key 1] & [Value 1] or it should be converted as a column named [Key] & [Value].
A different approach
When I work with 3rd party databases I usually add a new database, on the same server/instance if possible, and then I use it for my own purposes, just to avoid conflicts with the DB owners.
In this case you could add a new table and periodically throw a process to update it with the new values.
You could use a table with all columns:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL PRIMARY KEY,
[V1] int NULL,
[V2] int NULL,
[V3] int NULL,
[V4] int NULL,
[V5] int NULL
);
or a table with Key/Value pairs and use a pivot to obtain the final result:
CREATE TABLE [FruitKeys]
(
[FruitID] int NOT NULL,
[Key] int NOT NULL,
[Value] int NOT NULL,
CONSTRAINT [PK_FruitKeys] PRIMARY KEY ([FruitID], [Key])
);
edited 2 hours ago
answered 6 hours ago
McNetsMcNets
17.7k5 gold badges27 silver badges62 bronze badges
17.7k5 gold badges27 silver badges62 bronze badges
This is an effective approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
– Andy
3 hours ago
add a comment
|
This is an effective approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
– Andy
3 hours ago
This is an effective approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
– Andy
3 hours ago
This is an effective approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
– Andy
3 hours ago
add a comment
|
McNets provided a reasonable approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
Because I'm working with hundreds of key/value pairs and also thousands of records in the table itself, I'm thinking about implementing a user defined function (below) to use as-needed in reports and queries where a specific key/value pair is needed (and known).
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
With this I'd be able to perform queries to include specified key/values, just not all key/values.
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
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment
|
McNets provided a reasonable approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
Because I'm working with hundreds of key/value pairs and also thousands of records in the table itself, I'm thinking about implementing a user defined function (below) to use as-needed in reports and queries where a specific key/value pair is needed (and known).
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
With this I'd be able to perform queries to include specified key/values, just not all key/values.
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
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment
|
McNets provided a reasonable approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
Because I'm working with hundreds of key/value pairs and also thousands of records in the table itself, I'm thinking about implementing a user defined function (below) to use as-needed in reports and queries where a specific key/value pair is needed (and known).
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
With this I'd be able to perform queries to include specified key/values, just not all key/values.
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
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
McNets provided a reasonable approach, but the splitting for every pair, while obviously necessary, is a pretty time consuming process. With 500+ key/value pairs for every record in the table, I'm not sure it's going to work for my purposes. It's probably a decent approach if there are fewer pairs and a small number of rows in the affected table.
Because I'm working with hundreds of key/value pairs and also thousands of records in the table itself, I'm thinking about implementing a user defined function (below) to use as-needed in reports and queries where a specific key/value pair is needed (and known).
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
With this I'd be able to perform queries to include specified key/values, just not all key/values.
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
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
answered 3 hours ago
AndyAndy
213 bronze badges
213 bronze badges
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
New contributor
Andy is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
add a comment
|
add a comment
|
Andy is a new contributor. Be nice, and check out our Code of Conduct.
Andy is a new contributor. Be nice, and check out our Code of Conduct.
Andy is a new contributor. Be nice, and check out our Code of Conduct.
Andy is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Database Administrators Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fdba.stackexchange.com%2fquestions%2f249593%2fmanipulating-a-column-containing-key-value-pairs%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
There are currently 517 keys, but that number occasionally grows. The name of the database is not emdb.
– Andy
7 hours ago