Concatening in a specific order given by a number - sql

I have a table like this:
I want to concatenate the Product name in the given Product_order by ID.
Therefore, I should get something like: CC-TC-CA for ID 1.

you can use string_agg()- it'll work sql server 2016+
select id, string_Agg(product,',') as pname
from tablename
group by id
OR you can use stuff()
SELECT id,
STUFF((SELECT ',' + product
FROM tablename AS T1
WHERE T1.id = T2.id
FOR XML PATH('')), 1, 1, '')
FROM tablename AS T2
GROUP BY id

If you can use a stored procedure instead of a single query the += operator can do what you're looking for.
DECLARE #Product_order VARCHAR(100) = '';
SELECT #Product_order += Product + '-' FROM [table] WHERE id = 1 ORDER BY Product_Order;
SELECT SUBSTRING(#Product_order, 0, LEN(#Product_order));
Update: I've learned that returning multiple rows and using in an assignment in the select clause is unsupported behavior in SQL Server.

Related

SQL Server 2016 - Transpose column of integers to row by day

I need to transpose one of the columns in the data date to a row of string and group by 2 other columns. My sample data consists of the following data:
I need the result to look like this:
That is all the LNs in one row per Employee code, per day.
I tried the below code -
DECLARE #Process_Conditions_Loans VARCHAR(500)
SELECT
t1.EmplCode,
t1.LogDate,
#Process_Conditions_Loans = CONCAT(COALESCE(#Process_Conditions_Loans + ',', ''),PS2)
FROM
#temp t1
WHERE
LN IS NOT NULL
GROUP BY
EmplCode, LogDate
But I am getting an error
A SELECT statement that assigns a value to a variable must not be combined with data-retrieval operations.
I can not use group_concat since I am using SQL Server 2016.
Any help would be great appreciated.
Thanks,
JH
You can use the older form of string aggregation:
select emplcode, logdate,
stuff( (select concat(', ', ln)
from t
where t.emplcode = el.emplcode and t.logdate = el.logdate
order by ln
for xml path ('')
), 1, 2, ''
)
from (select distinct emplcode, logdate
from t
) el

How to combine return results of query in one row

I have a table that save personnel code.
When I select from this table I get 3 rows result such as:
2129,3394,3508,3534
2129,3508
4056
I want when create select result combine in one row such as:
2129,3394,3508,3534,2129,3508,4056
or distinct value such as:
2129,3394,3508,3534,4056
You should ideally avoid storing CSV data at all in your tables. That being said, for your first result set we can try using STRING_AGG:
SELECT STRING_AGG(col, ',') AS output
FROM yourTable;
Your second requirement is more tricky, and we can try going through a table to remove duplicates:
WITH cte AS (
SELECT DISTINCT VALUE AS col
FROM yourTable t
CROSS APPLY STRING_SPLIT(t.col, ',')
)
SELECT STRING_AGG(col, ',') WITHIN GROUP (ORDER BY CAST(col AS INT)) AS output
FROM cte;
Demo
I solved this by using STUFF and FOR XML PATH:
SELECT
STUFF((SELECT ',' + US.remain_uncompleted
FROM Table_request US
WHERE exclusive = 0 AND reqact = 1 AND reqend = 0
FOR XML PATH('')), 1, 1, '')
Thank you Tim

Find "LIKE" duplicates in MSSQL

I got a MSSQL database and there's this column with IDs.
Some are {}-wrapped around the ID and some are not.
I need to find out if there are duplicate entries like:
'{abcd}' and 'abcd' in one column.
Obviously I dont know 'abcd'...
Is there a simple way of joining the same column and searching for "LIKE" duplicates? Inner Join is not working for me...
You could do something like this:
SELECT Id
FROM TableName AS T0
WHERE EXISTS
(
SELECT 1
FROM TableName AS T1
WHERE T0.Id = '{' + T1.Id + '}'
-- Uncomment the next row if you want all duplicates (with or without brackets):
-- OR '{' + T0.Id + '}' = T1.Id
)
This will return all the records where the id is wrapped by curly brackets and has duplicate id just without the brackets.
You can also do like
CREATE TABLE T(
ID VARCHAR(25)
);
INSERT INTO T VALUES
('abc'),
('{abc}'),
('def'),
('ghi'),
('{ghi}');
SELECT *
FROM
(
SELECT TRIM(TRANSLATE(ID, '{}', ' ')) ID
FROM T
) TT
GROUP BY ID
HAVING COUNT(ID) > 1;
-- Or you can also do
SELECT *
FROM
(
SELECT REPLACE(REPLACE(ID, '{', ''), '}', '') ID
FROM T
) TT
GROUP BY ID
HAVING COUNT(ID) > 1;
Note that TRANSLATE() and TRIM() functions is available only in SQL Server 2017

Replace Matching Row String From Another Column

I have this table:
ID NewName OldName Link
1 NewName1 OldName1 OldName2|OldName3
2 NewName2 OldName2 OldName1|OldName3
3 NewName3 OldName3 OldName1|OldName2
What I want to happen is to change all the OldName on the Link column to the NewName. Like this:
ID NewName OldName Link
1 NewName1 OldName1 NewName2|NewName3
2 NewName2 OldName2 NewName1|NewName3
3 NewName3 OldName3 NewName1|NewName2
Can anyone suggest what's the best way to do this?
You are looking to Change the value of Link according OldNames to its with New Names:
First you will need to split your Link data delimited by |into row & then Join with Your Table
SELECT TTT.ID,
TTT.[NewName],
TTT.OldName,
[Link] = STUFF(
(
SELECT
'|'+[Link]
FROM
(
SELECT AA.ID,
AA.[NewName],
AA.OldName,
T.[NewName] [Link]
FROM
(
SELECT ID,
NewName,
OldName,
split.x.value('.', 'NVARCHAR(MAX)') DATA
FROM
(
SELECT ID,
NewName,
OldName,
CAST('<M>'+REPLACE(Link, '|', '</M><M>')+'</M>' AS XML) AS String
FROM <table_name>
) AS a
CROSS APPLY String.nodes('/M') AS split(x)
) AA
INNER JOIN <table_name> T ON T.OldName = AA.DATA
) TT
WHERE TT.ID = TTT.ID FOR XML PATH('')
), 1, 1, '')
FROM <table_name> TTT;
Result :
ID NewName OldName Link
1 NewName1 OldName1 NewName2|NewName3
2 NewName2 OldName2 NewName1|NewName3
3 NewName3 OldName3 NewName1|NewName2
MSSQL:
To store delimited-values from other column/other sources to a specific row, you may use FOR XML PATH, look at this SO thread:
UPDATE YourTable
SET Link = SUBSTRING(
(
SELECT '|' + T2.NewName
FROM YourTable T2
WHERE '|'+T2.Link+'|' LIKE '%|'+YourTable.OldName+'|%'
FOR XML PATH ('')
), 2, 1000);
MYSQL:
If you want to store delimited-values from other column/other sources to a specific row, you may use mysql GROUP_CONCAT function:
UPDATE table t1
SET Link = (
SELECT GROUP_CONCAT(t2.NewName SEPARATOR '|')
FROM table t2 WHERE FIND_IN_SET(t2.OldName, REPLACE(t1.Link, '|', ','))
)
I assumed that you want to replace any old values in the Link column with its new value.
See the results in action on dbfiddle.uk
If the Link column have only two names always, then we try self joining twice to match the respective new names which should be used for replacement. The join condition is ugly, but this is the price paid for storing denormalized data in your table.
WITH cte AS
(
SELECT t1.Link, t2.NewName AS NewNameLeft, t3.NewName AS NewNameRight
FROM yourTable t1
LEFT JOIN yourTable t2
ON SUBSTRING(t1.Link, 1, CHARINDEX('|', t1.Link) - 1) = t2.OldName
LEFT JOIN yourTable t3
ON SUBSTRING(t1.Link,
CHARINDEX('|', t1.Link) + 1,
LEN(t1.Link) - CHARINDEX('|', t1.Link)) = t3.OldName
)
UPDATE cte
SET Link = NewNameLeft + '|' + NewNameRight
WHERE NewNameLeft IS NOT NULL AND NewNameRight IS NOT NULL;
Note that this answer assumes that each old name appears only once in the table. I default to not doing an update unless both left and right new names are found.
I guess you just need REPLACE
select id, NewName, OldName, replace(link, 'OldName', 'NewName') Link
from your_data
and if you need to do it directly in table then use
update your_data
set link = replace(link, 'OldName', 'NewName')

Is there a way to shorten this query?

I have a query like this:
SELECT Name,
REPLACE(RTRIM((
SELECT CAST(Score AS VARCHAR(MAX)) + ' '
FROM
(SELECT Name, Score
FROM table
WHERE
---CONDITIONS---
) AS InnerTable
WHERE (InnerTable.Name = OuterTable.Name) FOR XML PATH (''))),' ',', ') AS Scores
FROM table AS OuterTable
WHERE
---CONDITIONS---
GROUP BY Name;
As it can be seen, I am using the same set of conditions to derive the InnerTable and OuterTable. Is there a way to shorten this query? I am asking this because, sometime back, I saw a keyword USING in MySQL that simplified my life using which you can specify a query once and then use its alias for the rest of the query.
You could look at creating a Common Table Expression (CTE). That is your best bet for aliasing a select. Unfortunately I'm not sure how much shoerter it will make your query though it does prevent you from defining the where conditions twice. see below:
with temp as
(
SELECT Name, Score
FROM table
WHERE whatever = 'whatever'
)
SELECT Name,
REPLACE(RTRIM((
SELECT CAST(Score AS VARCHAR(MAX)) + ' '
FROM
(SELECT Name, Score
FROM temp ) AS InnerTable
WHERE (InnerTable.Name = OuterTable.Name) FOR XML PATH (''))),' ',', ') AS Scores
FROM temp AS OuterTable
GROUP BY Name;