coalesce on missing fields - sql

any idea why COALESCE is not returning the string 'emptyfield' at all?
SELECT
userid,
string_agg(COALESCE(configvalue, 'emptyfield'), '|')
FROM
oc_preferences
WHERE
configkey IN(
'email',
'quota',
'lastLogin',
'displayName'
)
GROUP BY
userid;
i'm getting mixed field order and i'm also missing NULL values
480f0c81-8090aa8f|1 GB|John Smith|john.smith#e-mail.com|1551376267
9094f888-aa4ef8ef|peter.calo#domain.com|1 GB|Peter Calo
34555345-76867888|Mary Aston|2 GB
but i expected something like
480f0c81-8090aa8f|1 GB|John Smith|john.smith#e-mail.com|1551376267
9094f888-aa4ef8ef|1 GB|Peter Calo|peter.calo#domain.com|emptyfield
34555345-76867888|2 GB|Mary Aston|emptyfield |emptyfield
the table looks like this
SELECT *
FROM oc_preferences
ORDER by userid
FETCH first 5 rows only
userid|appid|configkey|configvalue
480f0c81-8090aa8f|avatar|generated|true
480f0c81-8090aa8f|files|quota|1 GB
480f0c81-8090aa8f|settings|email|john.smith#e-mail.com
480f0c81-8090aa8f|user_ldap|displayName|John Smith
480f0c81-8090aa8f|user_ldap|homePath|
live example: https://www.db-fiddle.com/f/2rNofuyfKv3j3YLyRctE7U/1

Applying a custom order before aggregating may help with the mixed field values. I assume that for every user that you will have all configkeys even if their values are empty in this table.
See fiddle below:
Schema (PostgreSQL v11)
CREATE TABLE oc_preferences (
userid VARCHAR(17),
appid VARCHAR(9),
configkey VARCHAR(11),
configvalue VARCHAR(21)
);
INSERT INTO oc_preferences
(userid, appid, configkey, configvalue)
VALUES
('480f0c81-8090aa8f', 'avatar', 'generated', 'true'),
('480f0c81-8090aa8f', 'files', 'quota', '1 GB'),
('480f0c81-8090aa8f', 'settings', 'email', 'john.smith#e-mail.com'),
('480f0c81-8090aa8f', 'user_ldap', 'displayName', 'John Smith'),
('480f0c81-8090aa8f', 'user_ldap', 'homePath', '');
Query #1
WITH oc_preferences_sorted AS (
SELECT
userid,configvalue,
CASE
WHEN configkey='quota' THEN 1
WHEN configkey='displayName' THEN 2
WHEN configkey='email' THEN 3
WHEN configkey='lastLogin' THEN 4
END as custom_order
FROM
oc_preferences
WHERE
configkey IN(
'email',
'quota',
'lastLogin',
'displayName'
)
ORDER BY 3
)
SELECT
userid,
string_agg(COALESCE(configvalue, 'emptyfield'), '|')
FROM
oc_preferences_sorted
GROUP BY
userid;
| userid | string_agg |
| ----------------- | ------------------------------------- |
| 480f0c81-8090aa8f | 1 GB|John Smith|john.smith#e-mail.com |
COALESCE will not return the string 'emptyfield' if a record with that configkey does not exist as there is nothing to aggregate as shown in the example above.
The following query creates all possible values and continues with your approach to create the concatenated field
WITH user_detaults AS (
SELECT
userid,
configkey
FROM
(SELECT DISTINCT userid from oc_preferences) users
INNER JOIN
(
SELECT 'quota' as configkey UNION ALL
SELECT 'displayName' as configkey UNION ALL
SELECT 'email' as configkey UNION ALL
SELECT 'lastLogin' as configkey
) keys ON 1=1
),
oc_preferences_sorted AS (
SELECT
u.userid,op.configvalue,
CASE
WHEN u.configkey='quota' THEN 1
WHEN u.configkey='displayName' THEN 2
WHEN u.configkey='email' THEN 3
WHEN u.configkey='lastLogin' THEN 4
END as custom_order
FROM
oc_preferences op
RIGHT JOIN
user_detaults u on u.userid = op.userid AND
u.configkey = op.configkey
ORDER BY 3
)
SELECT
userid,
string_agg(COALESCE(configvalue, 'emptyfield'), '|')
FROM
oc_preferences_sorted
GROUP BY
userid;
| userid | string_agg |
| ----------------- | ------------------------------------------------ |
| 480f0c81-8090aa8f | 1 GB|John Smith|john.smith#e-mail.com|emptyfield |
View on DB Fiddle
Recommended Edit
WITH user_detaults AS (
SELECT
userid,
configkey
FROM
(SELECT DISTINCT userid from oc_preferences) users
CROSS JOIN
(
SELECT 'quota' as configkey UNION ALL
SELECT 'displayName' as configkey UNION ALL
SELECT 'email' as configkey UNION ALL
SELECT 'lastLogin' as configkey
) keys
),
oc_preferences_sorted AS (
SELECT
u.userid,op.configvalue,
CASE
WHEN u.configkey='quota' THEN 1
WHEN u.configkey='displayName' THEN 2
WHEN u.configkey='email' THEN 3
WHEN u.configkey='lastLogin' THEN 4
END as custom_order
FROM
oc_preferences op
RIGHT JOIN
user_detaults u on u.userid = op.userid AND
u.configkey = op.configkey
)
SELECT
userid,
string_agg(COALESCE(configvalue, 'emptyfield') , '|' ORDER BY custom_order )
FROM
oc_preferences_sorted
GROUP BY
userid;
DB Fiddle

Related

Select data from tables when needed columns are stored as records in a different table

An app is developed where a user picks what data he wants to see in a report. Having data as
ReportDataValues
ID
TableName
ColumnName
1
customer
first_name
2
address
zip_code
Customer
ID
first_name
last_name
address_id
1
joe
powell
1
2
andy
smith
2
Address
ID
street
zip_code
1
main ave.
48521
2
central str.
56851
is it possible using generic SQL mechanisms (PIVOT, UNPIVOT or other way) to select such data from only specified table.column pairs in DataValues table as rows so the query is compatible with SQL Server and Oracle and is not using dynamic execution of generated statements (like EXEC(query) or EXECUTE IMMEDIATE (query) ), so the result would be like
Col1
Col2
joe
48521
andy
56851
Later SQL statement will be used in a SAP Crystal Reports reporting engine.
In Oracle, join the customer and address tables to every row of reportdatavalues and then use a CASE expression to correlate the expected value with the table columns and pivot:
SELECT col1, col2
FROM (
SELECT c.id,
r.id AS value_id,
CASE
WHEN r.tablename = 'customer' AND r.columnname = 'id'
THEN TO_CHAR(c.id)
WHEN r.tablename = 'customer' AND r.columnname = 'first_name'
THEN c.first_name
WHEN r.tablename = 'customer' AND r.columnname = 'last_name'
THEN c.last_name
WHEN r.tablename = 'address' AND r.columnname = 'street'
THEN a.street
WHEN r.tablename = 'address' AND r.columnname = 'zip_code'
THEN TO_CHAR(a.zip_code)
END AS value
FROM customer c
INNER JOIN address a
ON a.id = c.address_id
CROSS JOIN ReportDataValues r
)
PIVOT (
MAX(value) FOR value_id IN (1 AS col1, 2 AS col2)
)
Which, for the sample data:
CREATE TABLE ReportDataValues (ID, TableName, ColumnName) AS
SELECT 1, 'customer', 'first_name' FROM DUAL UNION ALL
SELECT 2, 'address', 'zip_code' FROM DUAL;
CREATE TABLE Customer (ID, first_name, last_name, address_id) AS
SELECT 1, 'joe', 'powell', 1 FROM DUAL UNION ALL
SELECT 2, 'andy', 'smith', 2 FROM DUAL;
CREATE TABLE Address (ID, street, zip_code) AS
SELECT 1, 'main ave.', 48521 FROM DUAL UNION ALL
SELECT 2, 'central str.', 56851 FROM DUAL;
Outputs:
COL1
COL2
joe
48521
andy
56851
fiddle

Big query find data that could be in multiple columns

I have a table with the following data
id|task1_name|task1_date|task2_name|task2_date
1,breakfast,1/1/20,,
2,null,null,breakfast,,1/1/20
3,null,null,lunch,,1/1/20
4,dinner,1/1/20,lunch,1/1/10
I'd like to build a view that always displayed the task names in the same column or null if it could not be found in any of the columns e.g.
id|dinner_date|lunch_date|breakfast_date
1,1/1/20, null, null
2,null, null, 1/1/20
2,1/1/20, 1/1/10, null
I've tried using a nested IF statement e.g.
SELECT *
IF(task_1_name = 'dinner', task1_date, IF(task2_date = 'dinner', task2_date, NULL)) as `dinner_date`
FROM t
But as there are 50 or so columns in the real dataset, this seems like a stupid solution and would get complex very quickly, is there a smarter way here?
One method uses case expressions:
select t.*,
(case when task1_name = 'dinner' then task1_date
when task2_name = 'dinner' then task2_date
when task3_name = 'dinner' then task3_date
end) as dinner_date
from t;
Below is for BigQuery Standard SQL and generic enough to addresses concerns expressed in question. You don't need to know in advance number of columns and tasks names (although they should not have , or : which should not be a big limitation here and can be addressed if needed)
#standardSQL
CREATE TEMP TABLE ttt AS
SELECT id,
SPLIT(k, '_')[OFFSET(0)] task,
MAX(IF(SPLIT(k, '_')[OFFSET(1)] = 'name', v, NULL)) AS name,
MAX(IF(SPLIT(k, '_')[OFFSET(1)] = 'date', v, NULL)) AS DAY
FROM (
SELECT id,
TRIM(SPLIT(kv, ':')[OFFSET(0)], '"') k,
TRIM(SPLIT(kv, ':')[OFFSET(1)], '"') v
FROM `project.dataset.table` t,
UNNEST(SPLIT(TRIM(TO_JSON_STRING(t), '{}'))) kv
WHERE TRIM(SPLIT(kv, ':')[OFFSET(0)], '"') != 'id'
AND TRIM(SPLIT(kv, ':')[OFFSET(1)], '"') != 'null'
)
GROUP BY id, task;
EXECUTE IMMEDIATE '''
SELECT id, ''' || (
SELECT STRING_AGG(DISTINCT "MAX(IF(name = '" || name || "', day, NULL)) AS " || name || "_date")
FROM ttt
) || '''
FROM ttt
GROUP BY 1
ORDER BY 1
'''
Note; the assumption here is only about columns name to be task<N>_name and task<N>_date
If to apply to sample data (similar) to yours in question
WITH `project.dataset.table` AS (
SELECT 1 id, 'breakfast' task1_name, '1/1/21' task1_date, NULL task2_name, NULL task2_date UNION ALL
SELECT 2, NULL, NULL, 'breakfast', '1/1/22' UNION ALL
SELECT 3, NULL, NULL, 'lunch', '1/1/23' UNION ALL
SELECT 4, 'dinner', '1/1/24', 'lunch', '1/1/10'
)
output is
Row id breakfast_date lunch_date dinner_date
1 1 1/1/21 null null
2 2 1/1/22 null null
3 3 null 1/1/23 null
4 4 null 1/1/10 1/1/24
Here is another solution which doesn't use dynamic SQL, doesn't rely on specific column names and works with arbitrary number of columns:
WITH table AS (
SELECT 1 id, 'breakfast' task1_name, '1/1/21' task1_date, NULL task2_name, NULL task2_date UNION ALL
SELECT 2, NULL, NULL, 'breakfast', '1/1/22' UNION ALL
SELECT 3, NULL, NULL, 'lunch', '1/1/23' UNION ALL
SELECT 4, 'dinner', '1/1/24', 'lunch', '1/1/10'
)
SELECT
REGEXP_EXTRACT(f, r'breakfast\, ([^\,\)]*)'),
REGEXP_EXTRACT(f, r'lunch\, ([^\,\)]*)'),
REGEXP_EXTRACT(f, r'dinner\, ([^\,\)]*)')
FROM (
SELECT FORMAT("%t", t) f FROM table t
)

Selecting id's where all rows match

I have two tables : DOCUMENT and METADATA. DOCUMENT stores an ID and some informations we're not interested in, METADATA stores "tags" for those documents. A tag is composed of a key and a value.
So for one document, there is only one entry in the DOCUMENT table, but possibly many in the METADATA table.
Now what I need is to pass a set of keys/values, and retrieve from the METADATA table only the documents that match ALL the keys/values. Which means inspecting different rows "at the same time", well, I don't really know how to do it.
Quick example:
META_KEY | META_VALUE | META_DOCUMENT_ID
----------------------------------------
Firstname| Chris | 1
Lastname | Doe | 1
Firstname| Chris | 2
Lastname | Moe | 2
So if I query with the following tags : "Firstname"="Chris", "Lastname"="Doe", I want 1 as result. If I only specify "Firstname"="Chris" I want both 1 and 2 as results.
Thanks a lot for any help !
EDIT :
How about something where I count the number of tags that have to match ?
Like this :
select meta_document_id, count(*) from metadata where (meta_key = 'Firstname' and meta_value = 'Chris') or (meta_key = 'Lastname' and meta_value = 'Doe') group by meta_document_id
With the count(*) I can easily find out if all the input key/value pairs have matched. How would that run performance-wise ?
Well, you are employing a database model named "key-value" or "Entity-attributte-value".
This is usually not a best choice, you can read more on this in these questions:
Key/Value pairs in a database table
Key value pairs in relational database
You need two separate queries for these two cases like this:
SELECT distinct META_DOCUMENT_ID
FROM METADATA
WHERE meta_key = 'Firstname' and meta_value = 'Chris'
SELECT distinct m1.META_DOCUMENT_ID
FROM METADATA m1
JOIN METADATA m2
ON m1.META_DOCUMENT_ID = m2.META_DOCUMENT_ID
WHERE m1.meta_key = 'Firstname' and m1.meta_value = 'Chris'
AND m2.meta_key = 'Lastname' and m2.meta_value = 'Doe'
EDIT:
I suppose I'll have to join N times the table for N key/value pairs ?
This could be done without a join, for example like below (assuming that each id has no more than 1 meta_key value):
SELECT META_DOCUMENT_ID
FROM METADATA
WHERE (meta_key, meta_value) IN
( ('Firstname' ,'Chris'), ('Lastname', 'Doe' ) )
GROUP BY META_DOCUMENT_ID
HAVING COUNT(*) = 2 /* 2 means that we are looking for 2 meta keys */
How is that going to run performance-wise ?
Terribly. See an explanation from links above about this model.
This query must in many cases do a full table scan (especially when a number of attributes/keys we are looking for is more than a few), count values for each id, then pick these id that have count = 2.
In a normalized model this is a simple query that can use indexes to quickly pick only these few rows with firstname = 'Chris'
SELECT *
FROM table
WHERE firstname = 'Chris' and lastname = 'Doe'
Oracle Setup:
CREATE TYPE KEY_VALUE_PAIR IS OBJECT (
KEY VARCHAR2(50),
VALUE VARCHAR2(50)
);
/
CREATE TYPE KEY_VALUE_TABLE IS TABLE OF KEY_VALUE_PAIR;
/
CREATE TABLE meta_data ( meta_key, meta_value, meta_document_id ) AS
SELECT 'Firstname', 'Chris', 1 FROM DUAL UNION ALL
SELECT 'Lastname', 'Doe', 1 FROM DUAL UNION ALL
SELECT 'Phonenumber', '555-2368', 1 FROM DUAL UNION ALL
SELECT 'Firstname', 'Chris', 2 FROM DUAL UNION ALL
SELECT 'Lastname', 'Moe', 2 FROM DUAL UNION ALL
SELECT 'Phonenumber', '555-0001', 2 FROM DUAL;
Query:
SELECT meta_document_id
FROM (
SELECT meta_document_id,
CAST(
COLLECT(
KEY_VALUE_PAIR( meta_key, meta_value )
) AS KEY_VALUE_TABLE
) AS key_values
FROM meta_data
GROUP BY meta_document_id
)
WHERE KEY_VALUE_TABLE(
-- Your values here:
KEY_VALUE_PAIR( 'Firstname', 'Chris' ),
KEY_VALUE_PAIR( 'Lastname', 'Doe' )
)
SUBMULTISET OF key_values;
Output:
META_DOCUMENT_ID
------------------
1
Update - Reimplementing the meta data table using a nested table:
Oracle Setup:
CREATE TYPE KEY_VALUE_PAIR IS OBJECT (
META_KEY VARCHAR2(50),
META_VALUE VARCHAR2(50)
);
/
CREATE TYPE KEY_VALUE_TABLE IS TABLE OF KEY_VALUE_PAIR;
/
CREATE TABLE meta_data (
meta_document_id INT,
key_values KEY_VALUE_TABLE
) NESTED TABLE key_values STORE AS meta_data_key_values;
CREATE UNIQUE INDEX META_DATA_KEY_VALUES_IDX ON META_DATA_KEY_VALUES (
NESTED_TABLE_ID,
META_KEY,
META_VALUE
);
/
-- Insert everything in one go:
INSERT INTO META_DATA VALUES(
1,
KEY_VALUE_TABLE(
KEY_VALUE_PAIR( 'Firstname', 'Chris' ),
KEY_VALUE_PAIR( 'Lastname', 'Doe' ),
KEY_VALUE_PAIR( 'Phonenumber', '555-2368' )
)
);
-- Insert everything in bits:
INSERT INTO meta_data VALUE ( 2, KEY_VALUE_TABLE() );
INSERT INTO TABLE( SELECT key_values FROM meta_data WHERE meta_document_id = 2 )
( meta_key, meta_value ) VALUES( 'Firstname', 'Chris' );
INSERT INTO TABLE( SELECT key_values FROM meta_data WHERE meta_document_id = 2 )
( meta_key, meta_value ) VALUES( 'Lastname', 'Moe' );
INSERT INTO TABLE( SELECT key_values FROM meta_data WHERE meta_document_id = 2 )
( meta_key, meta_value ) VALUES( 'Phonenumber', '555-0001' );
--Select all the key-value pairs:
SELECT META_DOCUMENT_ID,
META_KEY,
META_VALUE
FROM META_DATA md,
TABLE( md.KEY_VALUES );
Query:
The changes above let you simplify the query a lot:
SELECT META_DOCUMENT_ID
FROM meta_data
WHERE KEY_VALUE_TABLE(
-- Your values here:
KEY_VALUE_PAIR( 'Firstname', 'Chris' ),
KEY_VALUE_PAIR( 'Lastname', 'Doe' )
)
SUBMULTISET OF key_values;
If you know in advance all the possible TAGS, an approach could be with some PIVOT:
with METADATA (META_KEY, META_VALUE, META_DOCUMENT_ID) as
(
select 'Firstname', 'Chris',1 from dual union all
select 'Lastname', 'Doe',1 from dual union all
select 'Firstname', 'Chris',2 from dual union all
select 'Lastname', 'Moe',2 from dual
)
select *
from metadata
PIVOT ( max (META_VALUE ) FOR (META_KEY) IN ('Firstname' AS Firstname, 'Lastname' AS Lastname))
where Firstname = 'Chris' /* and Lastname ='Doe' ...*/

How to make a select statement to return "NULLs" if the value is a repetition in SQL

Lets take we have:
SELECT Name, Surname, Salary, TaxPercentage
FROM Employees
returns:
Name |Surname |Salary |TaxPercentage
--------------------------------------
Moosa | Jacobs | $14000 | 13.5
Temba | Martins | $15000 | 13.5
Jack | Hendricks | $14000 | 13.5
I want it to return:
Name |Surname | Salary |TaxPercentage
-------------------------------------------
Moosa | Jacobs | $14000 | NULL
Temba | Martins | $15000 | NULL
Jack | Hendricks| $14000 | 13.5
Since TaxPercentage's value is repeated, I want it appear only once at the end.
In sql server 2012 and above you can use the Lead window function to get the value of the next row. Assuming you have some way to sort the data (like an identity column), you can use this to your advantage:
SELECT Name,
Surname,
Salary,
CASE WHEN TaxPercentage = LEAD(TaxPercentage) OVER (ORDER BY Id) THEN
NULL
ELSE
TaxPercentage
END As TaxPercentage
FROM Employees
ORDER BY Id
See fiddle (thanks to Lasse V. Karlsen)
You should have some way to order the data in order. In my example, I am using simple IDENTITY column, in your it could be primary key or date:
DECLARE #DataSource TABLE
(
[Name] VARCHAR(12)
,[Surname] VARCHAR(12)
,[Salary] VARCHAR(12)
,[TaxPercentage] DECIMAL(9,1)
--
,[RowID] TINYINT IDENTITY(1,1)
);
INSERT INTO #DataSource ([Name], [Surname], [Salary], [TaxPercentage])
VALUES ('Moosa', 'Jacobs', '$14000', '13.5')
,('Temba', 'Martins', '$15000', '13.5')
,('Jack', ' Hendricks', '$14000', '13.5')
,('Temba', 'Martins', '$15000', '1.5')
,('Jack', ' Hendricks', '$14000', '1.5')
,('Temba', 'Martins', '$15000', '23')
,('Jack', ' Hendricks', '$14000', '7')
,('Temba', 'Martins', '$15000', '7')
,('Jack', ' Hendricks', '$14000', '7')
SELECT [Name]
,[Surname]
,[Salary]
,[TaxPercentage]
,NULLIF([TaxPercentage], LEAD([TaxPercentage], 1, NULL) OVER (ORDER BY [RowID])) AS [NewTaxPercentage]
FROM #DataSource;
I need a column to sort rows like Id with identity column
;with cte as (
SELECT
Id, Name, Surname, Salary, TaxPercentage,
LEAD(TaxPercentage, 1, NULL) OVER (ORDER BY Id) AS NextValue
FROM Employees
)
select
Id, Name, Surname, Salary,-- TaxPercentage,
TaxPercentage = CASE WHEN TaxPercentage = NextValue THEN NULL ELSE TaxPercentage END
from cte
Please check SQL Lag() and Lead() functions for more detail on these new analytical functions
If for some reason you can't use LEAD() then this should work:
with T as (
SELECT
Name, Surname, Salary, TaxPercentage,
row_number() over (order by TaxPercentage /* ??? */) as rn
FROM Employees
)
select
Name, Surname, Salary,
nullif(
TaxPercentage,
(select t2.rn from T as t2 where t2.rn = t.rn + 1)
) as TaxPercentage
from T as t
Work with SQL Server >= 2008 if needed
http://sqlfiddle.com/#!3/ec020/1/0
Select o.Name, o.Surname, o.Salary
, TaxPercentage = case when o.id = 1 then o.TaxPercentage else null end
From (
Select Name, Surname, Salary, TaxPercentage
, id = row_number() over(partition by TaxPercentage order by Name, surname, Salary) -- update order...
From Employees as e
) as o
order by o.TaxPercentage, o.id desc

Transform two rows in one

We have an auditing system which logs all the changes that occur in all the system tables. Basically, this is how the AuditLog table looks like:
Currently I am creating a couple of sql views to query different kind information. Everything is ok except for one point. If you take a look at the image again, you will see I have a SubscriptionMetadata table which is a key-value pairs table with 2 fields (MetaName and MetaValue). What the immage shows is that the subscription has a new watermark which value is 'Licensed copy: Fullname, Company, V...'.
What I need is transform, in my view, these two rows in just one with the following form:
41 - Insert - SubscriptionMetadata - 2012-10-19 - 53DA4XXXXXX - Watermark - Licensed copy: Fullname, Company, V...
I really cannot imagine how I can do it or search for it neither.
There is another problem (I think), these rows comes always in that order: MetaName first and then MetaValue. That´s the only way to know they are related.
Could you help me, please?
While I cannot see your full table structure you can transform the data the following way. Both of these solutions will place the data in separate columns:
;with data(id, [action], [type], [date], [col], metatype, value) as
(
select 41, 'Insert', 'SubscriptionMetaData', '2012-10-19', '53DA4XXX','Metaname', 'Watermark'
union all
select 41, 'Insert', 'SubscriptionMetaData', '2012-10-19', '53DA4XXX','MetaValue', 'Licensed copy: Fullname, Company'
)
select id, action, type, date, col,
MAX(case when metatype = 'Metaname' then value end) Name,
MAX(case when metatype = 'MetaValue' then value end) Value
from data
group by id, action, type, date, col
See SQL Fiddle with Demo
Or you can use a PIVOT on the data to get the same result:
;with data(id, [action], [type], [date], [col], metatype, value) as
(
select 41, 'Insert', 'SubscriptionMetaData', '2012-10-19', '53DA4XXX','Metaname', 'Watermark'
union all
select 41, 'Insert', 'SubscriptionMetaData', '2012-10-19', '53DA4XXX','MetaValue', 'Licensed copy: Fullname, Company'
)
select *
from
(
select id, [action], [type], [date], [col], metatype, value
from data
) src
pivot
(
max(value)
for metatype in (Metaname, MetaValue)
) piv
See SQL Fiddle with Demo
Both produce the same result:
| ID | ACTION | TYPE | DATE | COL | NAME | VALUE |
-------------------------------------------------------------------------------------------------------------
| 41 | Insert | SubscriptionMetaData | 2012-10-19 | 53DA4XXX | Watermark | Licensed copy: Fullname, Company |
You can do this via a stored procedure or scalar-valued function using the coalesce function as follows:
DECLARE #Results NVARCHAR(MAX);
DECLARE #Token NVARCHAR(5) = '-'; -- separator token
SELECT #Results = coalesce(#Results + #Token,'') + t.MetaValue from (select * from TableName where MetaName = 'SubscriptionMetadata') t;
RETURN #Results; -- variable containing the concatenated values
Here is a working example. Please replace column names as required. Col3 = your string concatenating column.
SELECT t1.col1,t1.col2,
NameValue =REPLACE( (SELECT col3 AS [data()]
FROM mytable t2
WHERE t2.col1 = t1.col1
ORDER BY t2.col1
FOR XML PATH('')
), ' ', ' : ')
FROM mytable t1
GROUP BY col1,col2 ;
--DATA
1 | X | Name
1 | X | Value
--RESULTS
1 | X | Name:Value --Name Value pair here
EDIT: If you don't need concatenation (as per your comment)
SELECT t1.col1, t1.col2, t1.col3 NameColumn, t2.col3 ValueColumn
FROM (SELECT * FROM myTable WHERE col3 = 'Watermark') t1 JOIN
(SELECT * FROM myTable WHERE NOT (col3 = 'Watermark')) t2
ON t1.col1 = t2.col1