Concatenate the row values after join [duplicate] - sql

This question already has answers here:
How to concatenate text from multiple rows into a single text string in SQL Server
(47 answers)
Closed 8 years ago.
How to concatenate the row values after joining Table1 and table2.
Table 1:
-----
Col1
------
1
2
Table 2:
-----------------
Col1 Col2
-----------------
1 A
1 B
1 C
2 D
2 E
2 F
Desired Result:
-----------------
Col1 Col2
-----------------
1 A,B,C
2 D,E,F

Try this:
create table #table1(
col1 int
)
create table #table2(
col1 int,
col2 char(1),
)
insert into #table1
select 1 union all
select 2
insert into #table2
select 1, 'A' union all
select 1, 'B' union all
select 1, 'C' union all
select 2, 'D' union all
select 2, 'E' union all
select 2, 'F'
select
col1,
col2 =
stuff((
select
', ' + t2.col2
from #table2 t2
where
t2.col1 = t1.col1
group by t2.col2
for xml path(''), type).value('.', 'varchar(max)'
), 1, 2, '')
from #table1 t1
drop table #table1
drop table #table2

Mysql:
SELECT group_concat(table2.col2) FROM
table2 JOIN table1 ON table1.col1 = table2.col1
GROUP BY table2.col1

You can use cursor as the following cod.
checked for syntax only
create table #Desired_Result(col1 int,col2 varchar(20))
DECLARE cur cursor FAST_FORWARD READ_ONLY
FOR
SELECT col1,col2
DECLARE #d int
declare #l varchar(20)
declare #str1 varchar(30)=''
declare #str2 varchar(30)=''
OPEN cur
FETCH NEXT FROM cur INTO #d,#l
WHILE ##FETCH_STATUS=0
BEGIN
if #d=1
set #str1=#str1+#l+','
else
if #d=2
set #str2=#str2+#l+','
FETCH NEXT FROM cur INTO #d,#l
END
#str1=substring(#str1,1,len(#str1)-1)
#str2=substring(#str2,1,len(#str2)-1)
insert into #Desired_Result values (col1,col2)(1,#str1)
insert into #Desired_Result values (col1,col2)(2,#str2)
select * from #Desired_Result
Close cur
DEALLOCATE cur

Related

Removal of Cursor in SQL

Its always been discouraged to use cursor, which have been extensively used in our current stored procedures and replace them with set based queries. But this particular scenario is one, where I did not get solution to use set based query and forced to keep using the cursor. I am providing below sample code which represents the scenario:
DECLARE #temp varchar(10), #continuechar varchar(10)
DECLARE #table1 table (col1 varchar(10))
insert into #table1
select 'A' UNION
select 'B' UNION
select 'C' UNION
select 'D' UNION
select 'E' UNION
select 'F' UNION
select 'G'
DECLARE Cursor1 CURSOR for select Col1 from #table1
open Cursor1
FETCH NEXT from Cursor1 into #temp
WHILE ##FETCH_STATUS = 0
BEGIN
if #temp='A'
BEGIN
set #continuechar=#temp
END
if #temp='C'
BEGIN
set #continuechar=#temp
END
select #continuechar, #temp
FETCH NEXT from Cursor1 into #temp
END
CLOSE cursor1;
deallocate cursor1
Here in above sample code #continuechar variable is not getting set, every time cursor is getting executed. If #continuechar is getting set, then following select statement is providing result set with current value of #continuechar:
select #continuechar, #temp
if its not getting set, then its using the previously set value to provide result set.
Can we have set based queries to remove cursor from such scenario.
First I would add some id column to get stable sort. Then simply use windowed functions:
SUM() OVER() to calculate groups
FIRST_VALUE() OVER() to propagate first value across group
(present from SQL Server 2012, you could exchange it with MAX(continuechar) OVER(PARTITION BY grp) if necessary)
DECLARE #table1 table (id INT IDENTITY(1,1), col1 varchar(10))
insert into #table1
select 'A' UNION
select 'B' UNION
select 'C' UNION
select 'D' UNION
select 'E' UNION
select 'F' UNION
select 'G';
WITH cte AS (
SELECT id
,col1
,CASE WHEN col1 IN('A', 'C') THEN col1 END AS continuechar
,SUM(CASE WHEN col1 IN ('A', 'C') THEN 1 ELSE 0 END)
OVER(ORDER BY id) AS grp
FROM #table1
)
SELECT id, col1,
FIRST_VALUE(continuechar) OVER(PARTITION BY grp ORDER BY id) AS continuechar
FROM cte
ORDER BY id;
DBFiddle Demo
EDIT:
Quirky update This is for pure demo only. Do not use this method on production system:
DECLARE #table1 table (id INT IDENTITY(1,1) PRIMARY KEY, col1 varchar(10),
continue_char VARCHAR(10));
DECLARE #temp VARCHAR(10);
insert into #table1(col1)
select 'A' UNION
select 'B' UNION
select 'C' UNION
select 'D' UNION
select 'E' UNION
select 'F' UNION
select 'G';
UPDATE #table1
SET #temp = CASE WHEN col1 IN ('A','C') THEN col1 ELSE #temp END
,continue_char = #temp
OPTION(MAXDOP 1);
SELECT *
FROM #table1;
DBFiddle Demo2

SQL Server: Replace values in field using lookup other table

Let's say I have below table script
DECLARE #result TABLE
(
[ID] Int
,[Data] Varchar(500)
)
DECLARE #codes TABLE
(
[ID] Varchar(500)
,[FullNames] Varchar(500)
)
INSERT INTO #result
SELECT 1
,'[A]-[B]'
INSERT INTO #result
SELECT 2
,'[D]-[A]'
INSERT INTO #result
SELECT 3
,'[A]+[C]'
INSERT INTO #codes
SELECT 'A'
,'10'
INSERT INTO #codes
SELECT 'B'
,'20'
INSERT INTO #codes
SELECT 'C'
,'30'
INSERT INTO #codes
SELECT 'D'
,'40'
SELECT * FROM #result
SELECT * FROM #codes
Output of those are as below:
#result
ID Data
-- -------
1 [A]-[B]
2 [D]-[A]
3 [A]+[C]
#codes
ID FullNames
-- -------
A 10
B 20
C 30
D 40
Now I want output as below also:
ID Data
-- -----
1 10-20
2 40-10
3 10+30
Please help me.
Please note: Data columns also contains ([A]-[B]+[D])*[C]
I found similar solution on https://stackoverflow.com/a/26650255/8454103 which is for your reference.
Try like this;
select Output from (
select Data, c1.FullNames as LeftSideName, c2.FullNames as RightSideName, LeftSide, RightSide, REPLACE(REPLACE(Data,'[' + LeftSide + ']',c1.FullNames),'[' + RightSide + ']',c2.FullNames) as Output from (
select r.ID, Data,SUBSTRING(Data, 2, 1) LeftSide ,SUBSTRING(Data, 6, 1) RightSide from #result r ) Result
inner join #codes c1 ON Result.LeftSide = c1.ID
inner join #codes c2 ON Result.RightSide = c2.ID)
Records
Output:
10-20
10+30
40-10
The query can perform replacing dynamically A,B,C,D,E according to #codes table etc.
Try this:
select ID, REPLACE(REPLACE(REPLACE(REPLACE(Data,
'[A]', (select FullNames from #codes where ID = 'A')),
'[B]', (select FullNames from #codes where ID = 'B')),
'[C]', (select FullNames from #codes where ID = 'C')),
'[D]', (select FullNames from #codes where ID = 'D'))
from #result

for loop in transact sql

I have a table that contains some data that needs to be updated. Let's assume that the table structure is
(Code, Col1, Col2, Col3)
I need to run an update on specific rows that have the Code value (let's say the values are (1,3,4,5,9,6,30,25,87,100)).
The Col3 value is Col1+Col2 and the Col1 values are (1001,1025,400,25,963,13,432,25,87,100).
I created the following SQL Query:
Declare #Col1 float
Declare #Code nvarchar
set #Col1 = 1001
set #Code = 1
update MyTable set
Col1 = #Col1,
Col3 = #Col1 + Col2
where Code = #Code
So, instead copying all this code after the Declare lines and manually assigning values, is it possible to create two arrays, one for Col1 values other for Code values and iterate through the Code and updating it dynamically?
Usually iteration over rows of data using loops or cursors considered as bad practice in SQL since it is much slower in most cases.
In your particular case there is no need to iterate over some "arrays" to perform your desired update.
Instead you can create temporary table like this:
create table #temp_table (Col1 float, Code nvarchar(10))
fill it with your data like:
insert into #temp_table (Col1, Code)
select 1001, '1'
union all
select 1025, '3'
... and so on
and then perform your update:
update MyTable set
Col1 = T1.Col1,
Col3 = T1.Col1 + Col2
from MyTable as T
inner join #temp_table as T1 on T.Code = T1.Code
You don't need a loop for this, you can just create a Cte (or temp table) with the values you want to update, and JOIN to it in an UPDATE statement:
;With ToUpdate (Code, Col1) As
(
Select 1, 1001 Union All
Select 3, 1025 Union All
Select 4, 400 Union All
Select 5, 25 Union All
Select 9, 963 Union All
Select 6, 13 Union All
Select 30, 432 Union All
Select 25, 25 Union All
Select 87, 87 Union All
Select 100, 100
)
Update T
Set Col1 = U.Col1,
Col3 = U.Col1 + Col2
From MyTable T
Join ToUpdate U On U.Code = T.Code

How to create loop based on value of row?

I have problem when I use my query bellow to have a looping inside the cursor.
data in table1 will be like this:
id | data
----|---------
A | 4
B | 2
C | 5
the result in table2 should be like this:
id | data
----|---------
A | 1
A | 1
A | 1
A | 1
B | 1
B | 1
C | 1
C | 1
C | 1
C | 1
C | 1
I have SQL query with cursor like this:
DECLARE #table2 table ( id VARCHAR(500), data INTEGER)
DECLARE Cur CURSOR FOR
SELECT id, data FROM table1
OPEN Cur
WHILE ( ##FETCH_STATUS = 0 )
BEGIN
DECLARE #LoopNum INTEGER
DECLARE #tempID VARCHAR(255)
DECLARE #tempDATA INTEGER
FETCH NEXT FROM Cur INTO #tempID, #tempDATA
set #LoopNum = 0
WHILE #LoopNum < #tempDATA
BEGIN
INSERT INTO table2 (id, data)
VALUES( #tempID, 1)
SET #LoopNum = #LoopNum + 1
END
END
CLOSE Cur
DEALLOCATE Cur
SELECT * FROM table2
but the query didn't work. is there something wrong with my query?
Thank you.
Use this query to the expected result.
CREATE TABLE #test
(id CHAR(1),data INT)
INSERT #test VALUES ('A',4)
INSERT #test VALUES('B',2)
INSERT #test VALUES('C',5);
SELECT s.id, 1 AS data
FROM #test s
INNER JOIN
master.dbo.spt_values t ON t.type='P'
AND t.number BETWEEN 1 AND s.data
Note: Refer this Why (and how) to split column using master..spt_values?
You actually don't need a loop
IF OBJECT_ID('TEMPDB..#TEMP') IS NOT NULL
DROP TABLE #TEMP
SELECT 'A' AS ID, 4 AS DATA
INTO #TEMP UNION
SELECT 'B', 2 UNION
SELECT 'C', 5
;WITH CTE AS
(
SELECT 1 AS NUMBER
UNION ALL
SELECT NUMBER + 1
FROM CTE
WHERE NUMBER < 100
)
SELECT T.ID, 1
FROM CTE C
INNER JOIN #TEMP T
ON C.NUMBER <= T.DATA
ORDER BY T.ID
Carefull that if you want ot generate a large set of numbers in the CTE it may become slower.
Use a Recursive CTE which will help you to loop through the records.
CREATE TABLE #test
(id CHAR(1),data INT)
INSERT #test
VALUES ('A',4),('B',2),('C',5);
WITH cte
AS (SELECT 1 AS da,id,data
FROM #test a
UNION ALL
SELECT da + 1,id,data
FROM cte a
WHERE da < (SELECT data
FROM #test b
WHERE a.id = b.id))
SELECT id,
1 AS data
FROM cte
ORDER BY id
i used two loops
1. for each row
2. for number for duplicate insert
SET NOCOUNT on;
DECLARE #t table(row int IDENTITY(1,1),id varchar(10),data int)
INSERT INTO #t
SELECT * from xyz
DECLARE #x table(id varchar(10),data int) --table to hold the new data
DECLARE #i int=(SELECT count (*) from xyz) --number of rows main table
DECLARE #y int --number of duplicate
DECLARE #p int=1 --number of rows
WHILE #i!=0 --loop until last row of main table
BEGIN
SET #y=(SELECT data FROM #t WHERE row=#p) --set #y for number of 'row duplicate'
WHILE #y!=0
BEGIN
INSERT INTO #x
SELECT id,1
FROM #t
WHERE row=#p
SET #y=#y-1
END
SET #p=#p+1
SET #i=#i-1
END
SELECT * FROM #x

Get top number of rows anyway

My requirement is like select top 5 rows from sql, if it contains only 2 rows then by default it shows some text say 'no data' in remaining 3 rows. Same for all conditions.
For i.e. Select top 5 rows, but it contains only 3 rows then query will return
Row1
Row2
Row3
No Data
No Data
Please try:
select top 5 Col
from(
select 0 srt, Col from YourTable
union all
select 1 srt, 'No Data' Col union all
select 1 srt, 'No Data' Col union all
select 1 srt, 'No Data' Col union all
select 1 srt, 'No Data' Col union all
select 1 srt, 'No Data' Col
)x
order by srt
this will be much more dynamic
declare #t TABLE (id int,TerminalID varchar(15))
declare #top int
set #top = 5
declare #c int --Holds Total
set #c = 0
while (#c < #top) begin
insert into #t (id, TerminalID) values (#c, 'No Data')
set #c = #c + 1
end
select top 5 * from
(
select top 5 TerminalID from AccessLog-- [where colo0 = ???]
union all
select TerminalID from #t
) x
/* assuming the column width is 8 characters and datatype is varchar */
DECLARE #NoDataTable AS TABLE(column1 VARCHAR(8))
DECLARE #i AS INT
SET #i = 0
WHILE(#i<5)
BEGIN
insert into #NoDataTable (column1)
values('No Data');
set #i = #i+1
end
select top 5 *
from
(
select column1 from TestTable
union all
select column1 from #NoDataTable
) as T
WITH cte AS
(
SELECT YourColumn FROM YourTable
UNION ALL
SELECT TOP 5 NULL FROM YourTable
)
SELECT TOP 5 COALESCE(YourColumn, 'No Data')
FROM cte