I have 2 columns
1st have string like: '7,8,9,0,3'
2nd have string like: '7,8,5,6,1'
I want output like this: 9,0,3 and 5,6,1 or both combined as 9,0,3,5,6,1
Convert the below code to SP:
DECLARE #Col1 VARCHAR(100)= '7,8,9,0,3', #Col2 VARCHAR(100)= '7,8,5,6,1'
DECLARE #Table1 Table (Col1 VARCHAR(100))
DECLARE #Table2 Table (Col2 VARCHAR(100))
INSERT INTO #Table1 VALUES ( #Col1)
INSERT INTO #Table2 VALUES (#Col2)
;with tmp( Data,Col1) as (
select CONVERT(VARCHAR(100),LEFT(Col1, CHARINDEX(',',Col1+',')-1)),
STUFF(Col1, 1, CHARINDEX(',',Col1+','), '')
FROM #Table1
union all
select CONVERT(VARCHAR(100),LEFT(Col1, CHARINDEX(',',Col1+',')-1)),
STUFF(Col1, 1, CHARINDEX(',',Col1+','), '')
FROM tmp
WHERE Col1>''
)
select ID = IDENTITY(INT,1,1),CONVERT(VARCHAR(100),Data) AS Col1
INTO #T1
from tmp
;with tmp( Data,Col2) as (
select CONVERT(VARCHAR(100),LEFT(Col2, CHARINDEX(',',Col2+',')-1)),
STUFF(Col2, 1, CHARINDEX(',',Col2+','), '')
FROM #Table2
union all
select CONVERT(VARCHAR(100),LEFT(Col2, CHARINDEX(',',Col2+',')-1)),
STUFF(Col2, 1, CHARINDEX(',',Col2+','), '')
FROM tmp
WHERE Col2>''
)
select ID = IDENTITY(INT,1,1),CONVERT(VARCHAR(100),Data) AS Col2
INTO #T2
from tmp
SELECT ID = IDENTITY(INT,1,1),Col1
INTO #T3
FROM
(
SELECT Col1
FROM #T1
EXCEPT
SELECT *
FROM
(
SELECT Col1
FROM #T1
INTERSECT
SELECT Col2
FROM #T2
)T3
UNION ALL
SELECT Col2
FROM #T2
EXCEPT
SELECT *
FROM
(
SELECT Col1
FROM #T1
INTERSECT
SELECT Col2
FROM #T2
)T4
)T5
SELECT DISTINCT STUFF((SELECT ',' + Col1
FROM #T3 T1
--WHERE T1.ID=T2.Id
FOR XML PATH ('')),1,1,'')
FROM #T3 T2
DROP TABLE #T1,#T2,#T3
Demo
What you need to do is full outer join on the same table between the 2 columns.
SELECT distinct A.name1,B.name2
FROM <TABLE> A full outer join <TABLE> B on A.name1 = B.name2
where A.name1 is null or B.name2 is null
Related
I working on a SQL query to obtain the common terms in strings in multiple rows and merge it with '/', For example
ID ROW1 ROW2 FINAL_RESULT
1 A A,B A/B
2 A/B A,C A/B,C
3 A,B A,C A/B,C
I'm working on with the below query, but it didn't give me the result what I'm expecting
update table1
set FINAL_RESULT =
case when ( CHARINDEX( ROW1 ,ROW2 ) > 0 ) then
ROW1 + '/' +Replace(ROW2 , ROW1 , '')
This method uses a full join of string_splits.
To find what's common or uncommon between the columns.
Then glues them together.
update t
set final_result = CONCAT(ca.common, '/' + ca.uncommon)
from table1 t
outer apply (
select
string_agg(common,',') as common,
string_agg(uncommon,',') as uncommon
from (
select
case when s1.value is null then s2.value
when s2.value is null then s1.value
end as uncommon
, case when s1.value = s2.value then s1.value
end as common
from string_split(replace(col1,'/',','),',') s1
full join string_split(replace(col2,'/',','),',') s2
on s1.value = s2.value
) q
) ca;
select *
from table1
id
col1
col2
final_result
1
A
A,B
A/B
2
A/B
A,C
A/B,C
3
A,B
A,C
A/B,C
db<>fiddle here
Or add a UDF for the purpose.
create function [dbo].[CombineCommonTerms]
(
#Value1 varchar(100),
#Value2 varchar(100),
#Delim char(1) = '/'
)
returns varchar(200)
as
begin
declare #tbl1 table (col varchar(30));
declare #tbl2 table (col varchar(30));
set #Value1 = replace(#Value1,#Delim,',');
declare #Xml1 xml = cast('<d>'+ replace(#Value1,',','</d><d>') +'</d>' as xml);
set #Value2 = replace(#Value2,#Delim,',');
declare #Xml2 xml = cast('<d>'+ replace(#Value2,',','</d><d>') +'</d>' as xml);
insert into #tbl1 (col)
select distinct ltrim(rtrim(a.split.value('.', 'varchar(30)') )) as col
from #Xml1.nodes('/d') a(split);
insert into #tbl2 (col)
select distinct ltrim(rtrim(a.split.value('.', 'varchar(30)'))) as col
from #Xml2.nodes('/d') a(split);
declare #Common varchar(100);
select #Common = concat(#Common+',', t1.col)
from #tbl1 t1
inner join #tbl2 t2
on t2.col = t1.col;
declare #Uncommon varchar(100);
select #Uncommon = concat(#Uncommon+',', coalesce(t1.col, t2.col))
from #tbl1 t1
full join #tbl2 t2
on t2.col = t1.col
where (t1.col is null or t2.col is null);
return concat(#Common, #Delim + #Uncommon);
end;
update t
set final_result = [dbo].[CombineCommonTerms](col1, col2, default)
from table1 t;
select *
from table1;
id
col1
col2
final_result
1
A
A,B
A/B
2
A/B
A,C
A/B,C
3
A,B
A,C
A/B,C
db<>fiddle here
Trying to replace some strings on my database where I've got two tables. The replacement on table_2 uses the results of the first replacement as an input:
Current state (string is only a stand-in, it can be anything, the important part is the dash):
|table_1 - col1| |table_2 - col1 |
---------------- ------------------------------
|string-1 | |text string-1 text string-3 |
|string-2 | |text string-3 string-2 t-ext|
|string-3 | |string-2 text string-3 te-xt|
Desired Result:
|table_1 - col1 | |table_2 - col1 |
----------------- ------------------------------
|string_1 | |text string_1 text string_3 |
|string_2 | |text string_3 string_2 t-ext|
|string_3 | |string_2 text string_3 te-xt|
Simply put I want to replace the - with _ in table_1 and also perform a corresponding replacement on table_2.
Came up with the first part, but I can't figure out the replacement part on table_2:
SELECT col1, REPLACE(col1, '-', '_') as Replacement
FROM table_1
where col1 like '%-%'
I need to do something like this (this code is INCORRECT):
SELECT REPLACE(col1,
SELECT [col1] FROM [table_1] where col1 like '%-%',
SELECT REPLACE([col1], '-', '_') FROM [table_1] where col1 like '%-%')
from table_2
For no more then 2 replacements
SELECT t2.col2, REPLACE(REPLACE(t2.col2,t1.col1,REPLACE(t1.col1, '-', '_')),t3.col1,REPLACE(t3.col1, '-', '_'))
FROM table_2 t2
JOIN table_1 t1 ON t2.col2 like '%' +t1.col1+'%' AND t1.col1 LIKE '%-%'
LEFT JOIN table_1 t3 ON t3.col1 <> t1.col1 AND t2.col2 LIKE '%'+t3.col1+'%' AND t3.col1 LIKE '%-%'
WHERE t2.col2 LIKE '%-%'
This is fully ad-hoc, no recursion needed:
DECLARE #table_1 TABLE(ID INT IDENTITY,col1 VARCHAR(100));
INSERT INTO #table_1 VALUES
('string-1'),('string-2'),('string-3');
DECLARE #table_2 TABLE(ID INT IDENTITY,col1 VARCHAR(100));
INSERT INTO #table_2 VALUES
('text string-1 text string-3'),('text string-3 string-2 t-ext'),('string-2 text string-3 te-xt');
--The first CTE replaces the value in t1
WITH t1New AS
(
SELECT ID AS t1_ID
,t1.col1 AS t1c1
,REPLACE(t1.col1,'-','_') AS new_t1c1
FROM #table_1 AS t1
)
--The second CTE splits the strings of t2 on the blanks
,t2Splitted AS
(
SELECT ID
,col1 AS t2c1
,CAST('<x>' + REPLACE(t2.col1,' ','</x><x>') + '</x>' AS XML) AS Casted
FROM #table_2 AS t2
)
--This CTE maps the values to the splitted parts
,Mapped AS
(
SELECT t1New.*
,t2Splitted.ID AS t2_ID
,ROW_NUMBER() OVER(PARTITION BY t2Splitted.ID ORDER BY (SELECT NULL)) AS PartIndex
,part.value('.','nvarchar(max)') AS Part
FROM t2Splitted
CROSS APPLY t2Splitted.Casted.nodes('/x') AS A(part)
LEFT JOIN t1New ON t1New.t1c1=part.value('.','nvarchar(max)')
)
--If there is a mapping, the new value is taken, else take the old value
,NewValues AS
(
SELECT *
,CASE WHEN t1c1 IS NOT NULL THEN new_t1c1 ELSE Part END AS newValue
FROM Mapped
)
--The final CTE re-concatenates the string with blanks in the original order
,Final AS
(
SELECT nv1.t2_ID
,(SELECT ' ' + nv2.newValue
FROM NewValues AS nv2
WHERE nv2.t2_ID=nv1.t2_ID
ORDER BY PartIndex
FOR XML PATH('')) AS FinalValue
FROM NewValues AS nv1
GROUP BY nv1.t2_ID
)
--This last value is used to update the original table
UPDATE t2 SET t2.col1=Final.FinalValue
FROM #table_2 AS t2
INNER JOIN Final ON Final.t2_ID=t2.ID
What's up to you: UPDATE t1, that's a one-liner and get rid of the trailing space in FinalValue :-)
SELECT * FROM #table_2
Variable based replacement can be done as replacing with a table.
DECLARE #Raw NVARCHAR(MAX) = '...';
SELECT #Raw= REPLACE(#Raw, P, R)
FROM (VALUES ('string-1', 'string_1'),
('string-2','string_2'),
('string-3','string_3'),
('string-4','string_4'),
) AS T(P, R);
To execute the same logic against table, think about some statements like
SELECT col1, MultipleReplace(col1, replacement_table(P, R))
FROM some_table
So create a function that accepts a string input and a replacement table. In order to pass table to function, we have to create a table type.
CREATE TYPE dbo.MulReplacements AS TABLE
(
Pattern NVARCHAR(MAX) NOT NULL,
Replacement NVARCHAR(MAX) NOT NULL
)
Then the function would be
CREATE FUNCTION dbo.MulReplace(
#string AS NVARCHAR(MAX),
#replacements AS dbo.MulReplacements READONLY
)
RETURNS NVARCHAR(MAX)
AS
BEGIN
DECLARE #result AS NVARCHAR(MAX) = #string;
SELECT #result = REPLACE(#result, R.Pattern, R.Replacement)
FROM #replacements AS R;
RETURN #result;
END
Put all together
DECLARE #replacement AS dbo.MulReplacements;
INSERT INTO #replacement
SELECT col1, REPLACE(col1, '-', '_')
FROM (VALUES ('string-1'), ('string-2'), ('string-3')) AS table_1(col1)
SELECT col1, dbo.Mulreplace(col1, #replacement)
FROM (VALUES ('text string-1 text string-3'), ('text string-3 string-2 t-ext'), ('string-2 text string-3 te-xt')) AS table_2(col1)
One way of doing it with Dynamic query. Replace the actual table name and column names (commented where to change).
DECLARE #colNames VARCHAR(MAX) = ''
SELECT #colNames = #colNames + ', [' + table1_Col1 + ']' FROM tableName1 -- Table1 Column and Table1 Name
DECLARE #ReqColNames VARCHAR(MAX) = STUFF(#colNames, 1, 1, '')
DECLARE #int int
SELECT #int = count(*) FROM tableName1 -- Table1 Name
DECLARE #replace varchar(max) = replicate('REPLACE(', #int) + 't2.table2_Col2' -- Table2 Column
DECLARE #replaceCols varchar(max) = ''
SELECT #replaceCols = #replaceCols + ', r.[' + table1_Col1 + '], replace(r.[' + table1_Col1 + '], ''-'', ''_''))' FROM tableName1 -- Table1 Column and Table1 Name
DECLARE #ReplaceString varchar(max) = #replace + #replaceCols
DECLARE #cmd varchar(max) = 'SELECT ' + #ReplaceString + ' FROM
(
SELECT * FROM tableName1
PIVOT
(MAX (table1_Col1) FOR table1_Col1 IN (' + #ReqColNames + ')) x
) r
CROSS JOIN tableName2 t2'
EXEC(#cmd)
Static Query: for above code (to show what the above dynamic code is generating):
Select replace(replace(replace(t2.table2_Col2
, r.[string-1], replace(r.[string-1], '-', '_'))
, r.[string-2], replace(r.[string-2], '-', '_'))
, r.[string-3], replace(r.[string-3], '-', '_'))
from
(
Select * from tableName1
PIVOT
(MAX (table1_Col1) FOR table1_Col1 IN ([string-1], [string-2], [string-3])) x
) r
CROSS JOIN tableName2 t2
Output:
text string_1 text string_3
text string_3 string_2 t-ext
string_2 text string_3 te-xt
Please see the following SQL code.
Declare #LookUp table (id int,val varchar(12))
insert into #LookUp values (1,'A'),(1,'D'),(1,'X'),(2,'B'),(2,'F')
Declare #summary table (id int,val varchar(2000))
------------------------------
Declare #value varchar(30)
Declare #start int = 1, #end int = (Select count(Distinct id) from #LookUp)
While #start <= #end
Begin
Set #value = ''
Select #value = #value + '|' + val From #LookUp Where id = #start
Insert into #summary
Select #start,Right(#value,Len(#value)-1)
Set #start = #start + 1
End
Select * From #summary
With the following query, I am grouping based on Id, and making the values with in a group, as comma-separated values.
Input:
Output:
I have done this using a loop, which is not performing very well when it comes to large amount of data.
Can we do this avoiding a loop?
Note: Assume that #lookup.id is continuous.
INSERT #summary(id, val)
SELECT
t.id,
STUFF((
select '|' + [val]
from #LookUp t1
where t1.id = t.id
for xml path(''), type
).value('.', 'varchar(max)'), 1, 1, '') [values]
FROM #LookUp t
GROUP BY t.id
Try this..
Declare #LookUp table (id int,val varchar(12))
insert into #LookUp values (1,'A'),(1,'D'),(1,'X'),(2,'B'),(2,'F')
select
id,
val =
stuff((
select
'| ' + t2.val
from #LookUp t2
where
t2.id = t1.id
group by t2.val
for xml path(''), type).value('.', 'varchar(max)'
), 1, 2, '')
from #LookUp t1
GROUP BY t1.id
Declare #LookUp table (id int,val varchar(12))
insert into #LookUp values (1,'A'),(1,'D'),(1,'X'),(2,'B'),(2,'F');
WITH A AS
(
SELECT DISTINCT id
FROM #LookUp
)
SELECT
A.id,
STUFF
(
(
SELECT ',' + val
FROM #LookUp B
WHERE B.id = A.id
FOR XML PATH('')
),
1, 1, ''
) AS val
FROM A
Declare #LookUp table (id int,val varchar(12))
insert into #LookUp values (1,'A'),(1,'D'),(1,'X'),(2,'B'),(2,'F')
SELECT DISTINCT a.id, stuff((select '|' + val
from #LookUp c
where c.id = a.id
for xml path('')),1,1,'') as vall
FROM #LookUp a
I'll add a solution based on a recursive common table expression. With small modifications it should work in DB2 too (you have to change the row_number() function to rownumber() and how the concatenation works in the second list (use || instead of the concat function).
DECLARE #LookUp TABLE (id int, val varchar(12))
INSERT INTO #LookUp VALUES (1,'A'),(1,'D'),(1,'X'),(2,'B'),(2,'F')
;WITH
list1(rn, id, val) AS
(
SELECT row_number() OVER (PARTITION BY id ORDER BY id), id, val
FROM #LookUp
),
list2 (id, val, idx) AS
(
SELECT id, val, 1
FROM list1
WHERE rn = 1
UNION ALL
SELECT
list2.id,
CAST(CONCAT(list2.val, ' | ', list1.val) AS varchar(12)),
list2.idx + 1
FROM list2
JOIN list1 ON list2.id = list1.id
WHERE list2.idx + 1 = list1.rn
)
SELECT l2.id, l2.val
FROM list2 l2
JOIN (SELECT id, MAX(rn) maxid FROM list1 GROUP BY id) a
ON l2.id = a.id AND l2.idx = a.maxid
ORDER BY l2.id
This outputs:
id val
1 A | D | X
2 B | F
I have a query
example
Title Description
A XYZ
A ABC
now i want a sql query so that i can get a single row
Output :
Title Description
A XYZ | ABC
Declare #tbl table(Title nvarchar(1),[Description] nvarchar(100))
Insert into #tbl values('A','XYZ');
Insert into #tbl values('A','ABC');
Insert into #tbl values('A','PQR');
DECLARE #CSVList varchar(100)
SELECT #CSVList = COALESCE(#CSVList + ' | ', '') +
[Description]
FROM #tbl
WHERE Title='A'
SELECT #CSVList
declare #table table (i int, a varchar(10))
insert into #table
select 1, 'ABC' union all
select 1, 'XYZ' union all
select 2, '123'
select t.i,
max(stuff(d.i, 1, 1, '')) [iList]
from #table t
cross
apply ( select '|' + a
from #table [tt]
where t.i = tt.i
for xml path('')
) as d(i)
group
by t.i;
In mysql there is a group_concat function, that can help you.
Use it like this:
SELECT Title,GROUP_CONCAT(Description) FROM table_name GROUP BY Title
The output will be
Title Description
A XYZ,ABC
Then you can replace "," with "|" if you want(it can be done with replace function)
For 2 rows you can self join in SQL Server. This avoids the assorted "concatenate rows into a column" tricks. You can use a LEFT JOIN and NULL handling too for 1 or 2 rows
SELECT
T1.Title,
T1.Description + '|' + T2.Description
FROM
MyTable T1
JOIN
MyTable T2 ON T1.Title = T2.Title
SELECT
T1.Title,
T1.Description + ISNULL('|' + T2.Description, '') --COALESCE for the pedants)
FROM
MyTable T1
LEFT JOIN
MyTable T2 ON T1.Title = T2.Title
If you are using SQL Server, try this: How to return 1 single row data from 2 different tables with dynamic contents in sql
Based on below 2 tables
declare #t1 table
(
Id int,
Title varchar(100),
RelatedId int
)
insert into #t1 values(1,'A',2)
insert into #t1 values(1,'A',3)
declare #t2 table
(
Id int,
Title varchar(100)
)
insert into #t2 values
(2,'B'),
(3,'C')
I am trying to get the below output
Id Title RelatedItems
---------------------------------
1 A 2 (B), 3 (C)
I tried the following:
select t1.Id,t1.Title, cast(t2.Id as varchar) + ' (' + t2.Title + ')' from #t1 as t1
left outer join #t2 as t2
on t1.RelatedId=t2.Id
But that gives 2 different rows. I want just one row with the data combined in the third column (as shown above). Pls. suggest.
Use:
SELECT DISTINCT
b.id,
b.title,
STUFF((SELECT ','+ CAST(t2.id AS VARCHAR(100)) + ' ('+ t2.title +')'
FROM t2
JOIN t1 a ON a.relatedid = t2.id
WHERE a.id = b.id
FOR XML PATH('')), 1, 1, '')
FROM t1 b