SQL SERVER STUFF FUNCTION SCENARIO - sql

In SQL Server, I have a table like this:
Col-1 Col-2 Col-3
------------------------
A 123 25.13
A 456 67.00
A 789 81.89
I want output like this:
Col-1 Col-2 Col-3
-----------------------------------
A 123,456,789 174.02

You can use FOR XML clause with STUFF() function :
select co1, stuff( (select distinct ','+cast(t1.col2 as varchar(255))
from table t1
where t1.col1 = t.col1
for xml path('')
), 1, 1, ''
) as col2,
sum(col3)
from table t
group by co1;

Sample Data :
DECLARE #T TABLE(Col1 VARCHAR(1),
Col2 INT,
Col3 FLOAT)
INSERT INTO #T( Col1, Col2, Col3 )
VALUES ('A',123,25.13),('A',456,67),('A',789,81.89)
Query :
;WITH Keys AS (SELECT Col1,
SUM(Col3) AS Col3
FROM #T
GROUP BY Col1)
SELECT Keys.Col1,
REPLACE(REPLACE(REPLACE( CONVERT (NVARCHAR(MAX),(SELECT T.Col2 AS A
FROM #T AS T
WHERE T.Col1 = Keys.Col1
FOR XML PATH(''))),'</A><A>',','),'<A>',''),'</A>','') AS Col2,
Keys.Col3
FROM Keys
Result :
Col1 Col2 Col3
A 123,456,789 174.02

Related

SQL command to break up a column into additional columns

I am trying to take one column in my table that has multiple values (up to 10) delimited by a "pipe" "|" in it and add the delimited values into additional columns in the table. Note (running on SQL SVR 2014).
Table ...
Col1 Col2 Col3
1 Tom 12345678|87654321|11111111|22222222|..... up to 10
2 Joe 14563467
3 Zac 12345678|87654321
I need the results of SQL to produce
Col1 Col2 Col3 Col4 Col5 Col6 Col7 Col8 Col9 ....
1 Tom 12345678 87654321 11111111 22222222
2 Joe 14563467
3 Zac 12345678 87654321
Any help is appreciated!
You can first normalize your data using the new STRING_SPLIT function into a derived table with the split columns stretched downward. Using that table you can PIVOT out based on Col1 to create the 5 columns basically pulling the data back up that was previously split down. Next, use that data as the source for you update back to the source table.
If you are not on SQL Server 2016 then you will need to replace STRING_SPLIT with an delimited string parser Table Value Function .
DECLARE #T TABLE(Col1 INT, Col2 NVARCHAR(50), Col3 NVARCHAR(50), Col4 NVARCHAR(50), Col5 NVARCHAR(50))
INSERT #T (COl1,Col2) VALUES (1,'12345678|87654321|11111111|22222222|')
INSERT #T (COl1,Col2) VALUES (2,'12345678')
INSERT #T (COl1,Col2) VALUES (3,'12345678|87654321|')
SELECT * FROM #T
;
WITH SplitData AS
(
SELECT
Col1,Col2 = S.Value,
RN = ROW_NUMBER()OVER( PARTITION BY Col1 ORDER BY (SELECT 1 AS X))
FROM
#T
CROSS APPLY STRING_SPLIT(Col2,'|') S
)
,UpdateData AS
(
SELECT
Col1, Col2=[1], Col3=[2], Col4=[3], Col5=[4]
FROM
(
SELECT Col1, Col2, RN FROM SplitData
) AS S
PIVOT(
MAX(Col2) FOR RN IN ([1], [2], [3], [4], [5])
) AS P
)
UPDATE L
SET L.Col1 = R.Col1, L.Col2=R.Col2, L.Col3=R.Col3, L.Col4=R.Col4, L.Col5 = R.Col5
FROM
#T L
INNER JOIN UpdateData R ON L.Col1 = R.Col1
SELECT * FROM #T
If u are sure max values count in Col3 is 10 u can use cross apply with split string on Col3 and select columns with splited value and row number partitioned by col1 and col2. Secound step is pivot this table by row
WITH SplitTable AS(
SELECT Col1
,Col2
,value
,ROW_NUMBER() OVER(PARTITION BY Col1, Col2 ORDER BY (SELECT NULL)) as rn
FROM YOUR_TABLE
CROSS APPLY STRING_SPLIT(Col3, '|') AS YT
)
SELECT Col1
,Col2
,[1]
,[2]
,[3]
,[4]
,[5]
,[6]
,[7]
,[8]
,[9]
,[10]
FROM SplitTable
PIVOT(
MAX(VALUE)
FOR RN IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10])
) as PVT

Not sure about the below scenario. Need a bit push. How can I solve below sql scenario

Input:
COL1 COL2
---------------
10 a
20 b
30 c
40 NULL
50 d
Desired output:
COL1 COL2
-----------------
10 a
20 a,b
30 a,b,c
40 a,b,c
50 a,b,c,d
Below is the solution I have tried so far. But this is not returning the desired output.
WITH CTE AS
(
SELECT
COL1,
LAG(COL2) OVER (ORDER BY COL1) AS prev_word,
COL2
FROM
dbo.Scenario
), CTE_A AS
(
SELECT
COL1, COL2, prev_word,
CONCAT(ISNULL(Prev_word, ''), ' ', ISNULL(COL2, '')) AS Con_Word
FROM
CTE
)
SELECT *
FROM CTE_A
One possible solution is the following statement. I assume, that the values in the COL1 column define the order, that is needed for the aggregation.
Table:
CREATE TABLE Data (
COL1 int,
COL2 varchar(1)
)
INSERT INTO Data (COL1, COL2)
VALUES
(10, 'a'),
(20, 'b'),
(30, 'c'),
(40, NULL),
(50, 'd')
Statement for SQL Server 2012:
SELECT d.COL1, STUFF(a.COL2, 1, 1, '') AS COL2
FROM Data d
CROSS APPLY (
SELECT CONCAT(',', COL2)
FROM Data
WHERE COL1 <= d.COL1 AND COL2 IS NOT NULL
ORDER BY COL2
FOR XML PATH('')
) a (COL2)
ORDER BY d.COL1
Statement for SQL Server 2017+ (using STRING_AGG() for string aggregation):
SELECT d1.COL1, STRING_AGG(d2.COL2, ',') WITHIN GROUP (ORDER BY d2.COL1) AS COL2
FROM Data d1
JOIN Data d2 ON d1.COL1 >= d2.COL1
WHERE d2.COL2 IS NOT NULL
GROUP BY d1.COL1
ORDER BY d1.COL1
Result:
COL1 COL2
10 a
20 a,b
30 a,b,c
40 a,b,c
50 a,b,c,d
try the following:
declare #t table (COL1 int, COL2 varchar(max))
insert into #t select 10, 'a'
insert into #t select 20, 'b'
insert into #t select 30, 'c'
insert into #t select 40, NULL
insert into #t select 50, 'd'
select COL1, STUFF(
(
SELECT DISTINCT ',' + COL2 FROM #t t2
WHERE t.COL1 >= t2.COL1 for xml path('')
),1,1,''
) AS COL2
from #t t
SELECT ID,STUFF((SELECT DISTINCT ',' + [Values] FROM Table_ t2
WHERE t.ID>= t2.ID for xml path('')),1,1,'') AS [Values]
FROM Table_ t

delete a row after comparing specific values with other rows in the same table

I have a table like this ( SQL SERVER )
col1 col2 col3 col4 col5
1 Goerge A B ++
2 Alex B B aa
2 Alex B B ++
now the second and the third rows have almost same values except in col5. in this case i want to delete one of them ( i want to be able to decide wich row from both of them i wanna delete ) like the one with value ++ so the table should look like this :
col1 col2 col3 col4 col5
1 Goerge A B ++
2 Alex B B aa
How can that be achieved?
Use CTE with ROW_NUMBER() to find (and delete) your duplicates
WITH CTE_Dup AS
(
SELECT *
, ROW_NUMBER OVER() PARTITION BY (Col1, Col2, Col3, Col4 ORDER BY Col5) RN
-- change ORDER BY criteria to make sure row you want to keep is 1st
FROM YourTable
)
DELETE --Try SELECT * to check before actually deleting
FROM CTE_Dup WHERE RN>1
It is not at all clear the logic you want here for determining which row is a "duplicate" but this example should show you a pretty straight forward way of handling this kind of thing.
declare #Something table
(
col1 int
, col2 varchar(10)
, col3 char(1)
, col4 char(1)
, col5 char(2)
)
insert #Something
values
(1, 'Goerge', 'A', 'B', '++')
, (2, 'Alex', 'B', 'B', 'aa')
, (2, 'Alex', 'B', 'B', '++')
;
with SortedValues as
(
select *
, ROW_NUMBER() over (partition by col1 order by col5 desc) as RowNum
from #Something
)
delete SortedValues
where RowNum > 1
select *
from #Something
You can try the below query. I grouped the columns and get the count of that and handled the conditions in the WHERE clause.
Query execution with the given sample data:
DECLARE #TestTable TABLE (col1 INT, col2 VARCHAR (20), col3 VARCHAR (20), col4 VARCHAR (20), col5 VARCHAR (20));
INSERT INTO #TestTable (col1, col2, col3, col4, col5) VALUES
(1, 'Goerge', 'A', 'B', '++'),
(2, 'Alex', 'B', 'B', 'aa'),
(2, 'Alex', 'B', 'B', '++');
SELECT * INTO #tmpResults FROM (
SELECT col1, col2, col3, col4, col5,
COUNT(*) OVER (PARTITION BY col1, col2, col3, col4 ORDER BY col1 ) AS CCnt
FROM #TestTable
) a
SELECT col1, col2, col3, col4, col5 FROM #tmpResults WHERE Ccnt = 1 AND Col5 = '++'
UNION
SELECT col1, col2, col3, col4, col5 FROM #tmpResults WHERE Ccnt > 1 AND Col5 <> '++';
DROP TABLE #tmpResults
Output:
col1 col2 col3 col4 col5
------------------------------------
1 Goerge A B ++
2 Alex B B aa

Merge rows into one row based on condition

I Have a table structure which similar to below
Create Table #Temp(Name varchar(10),Col1 int,Col2 int,Col3 int,Col4 int,Col5 int)
In this case table can have same name repeated but the other values to be different
So.
Sample values can be like
Insert Into #Temp
Values('ABC',1,0,0,1,1)
Insert Into #Temp
Values('ABC',1,0,1,1,0)
Insert Into #Temp
Values('ABC',1,0,1,1,0)
Insert Into #Temp
Values('DEF',0,0,0,1,0)
Insert Into #Temp
Values('DEF',1,0,1,1,1)
Insert Into #Temp
Values('DEF',1,1,0,1,1)
What I am trying to do here is select only one row for each name, but select the column with priority where it has value 1.
So the expected result in this case is
Name Col1 Col2 Col3 Col4 Col5
ABC 1 0 1 1 1
DEF 1 1 1 1 1
I have achieved it by doing something like below, which works absolutely fine. But is there any proper(easy) way of doing this.
SELECT Name,
(Select top 1 Col1
from #Temp T
Where T.Name=M.Name
Order By Col1 desc) as Col1,
(Select top 1 Col2
from #Temp T
Where T.Name=M.Name
Order By Col2 desc) as Col2,
(Select top 1 Col3
from #Temp T
Where T.Name=M.Name
Order By Col3 desc) as Col3,
(Select top 1 Col4
from #Temp T
Where T.Name=M.Name
Order By Col4 desc)as Col4,
(Select top 1 Col5
from #Temp T
Where T.Name=M.Name
Order By Col5 desc) as Col5
FROM #Temp M
Group By Name
It seems to me that you need to use MAX:
SELECT [Name],
MAX(Col1) Col1,
MAX(Col2) Col2,
MAX(Col3) Col3,
MAX(Col4) Col4,
MAX(Col5) Col5
FROM #Temp
GROUP BY [Name]
SELECT
Name,
Max(Col1) as Col1,
Max(Col2) as Col2,
Max(Col3) as Col3,
Max(Col4) as Col4,
Max(Col5) as Col5
FROM #Temp
GROUP BY Name

Switching 1 row with few columns into 1 column with few rows in MS SQL SERVER

i've got 1 row with many columns:
col1|col2|col3|...
and i want to have 1 column with many rows, like that:
col1
col2
col3
..
UNPIVOT if you're using version 2005+
http://www.tsqltutorials.com/unpivot.php
If SQL Server 2000 or lower...
How to rotate a table in SQL Server: http://support.microsoft.com/kb/175574
Take a look at UNPIVOT:
CREATE TABLE Table1 (col1 INT, col2 INT, col3 INT, col4 INT);
INSERT INTO Table1 (col1, col2, col3, col4) VALUES (1,2,3,4);
SELECT col, value FROM Table1
UNPIVOT (value FOR col IN (col1, col2, col3, col4)) AS unpvt
Result:
col value
col1 1
col2 2
col3 3
col4 4
If you don't want to know which value came from which column, only select value:
SELECT value FROM Table1
UNPIVOT (value FOR col IN (col1, col2, col3, col4)) AS unpvt