Update column, non distinct ID with secondary id? - sql

+-----+-----+------+----------+
| id | num | text | combo |
+-----+-----+------+----------+
| 126 | 1 | 12 | 12,58,94 |
| 126 | 2 | 58 | |
| 126 | 3 | 94 | |
| 130 | 1 | 28 | 28,45,64 |
| 130 | 2 | 45 | |
| 130 | 3 | 64 | |
+-----+-----+------+----------+
I have this table, where the column "combo" is currently null. I am trying to update it as such with data from "text" (from min value to max value in "num" and for each distinct id)
Any help formulating this query would be greatly appreciated, pardon my poor phrasing.

Test Data
CREATE TABLE #MyTable(id INT,num INT,text INT,combo VARCHAR(100))
GO
INSERT INTO #MyTable
VALUES
(126,1,12,NULL),
(126,2,58,NULL),
(126,3,94,NULL),
(130,1,28,NULL),
(130,2,45,NULL),
(130,3,64,NULL)
GO
Query
;With UpdateRecord
AS
(
SELECT * , rn = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY [text])
FROM #MyTable
),
UpdateValues
AS
(
SELECT ID, Num, STUFF(List.Numbers, 1 ,2 , '') Vals
, rn2 = ROW_NUMBER() OVER (PARTITION BY ID ORDER BY [text])
FROM #MyTable t CROSS APPLY
(
SELECT ', ' + CAST([text] AS VARCHAR) [text()]
FROM #MyTable
WHERE id = t.id
FOR XML PATH('')
)List(Numbers)
)
UPDATE UpdateRecord
SET combo = (SELECT TOP 1 Vals
FROM UpdateValues
WHERE id = UpdateRecord.id AND rn = UpdateRecord.rn
AND rn = 1)
Result
╔═════════╦═════╦══════╦════════════════╗
║ id ║ num ║ text ║ combo ║
╠═════════╬═════╬══════╬════════════════╣
║ 126 ║ 1 ║ 12 ║ 12, 58, 94 ║
║ 126 ║ 2 ║ 58 ║ NULL ║
║ 126 ║ 3 ║ 94 ║ NULL ║
║ 130 ║ 1 ║ 28 ║ 28, 45, 64 ║
║ 130 ║ 2 ║ 45 ║ NULL ║
║ 130 ║ 3 ║ 64 ║ NULL ║
╚═════════╩═════╩══════╩════════════════╝

You can use a recursive cte:
declare #SourceTable TABLE
(
id int, num int,textvalue varchar(255), combo varchar(255)
)
INSERT INTO #SourceTable(id, num, textvalue) VALUES
(126, 1, 12),(126, 2, 58),(126, 3, 94),(130, 1, 28),(130, 2, 45),(130, 3, 64)
Create a recursive cte that concatenates the values:
;with cte_ConcatenatedText(id, textvalue, num) AS
(
SELECT id, textvalue, num FROM #SourceTable WHERE num = 1
UNION ALL
SELECT st.id, cast(cte.textvalue + ',' + st.textvalue as varchar(255)), st.num
FROM #SourceTable st
INNER JOIN cte_ConcatenatedText cte ON cte.id = st.id and cte.num = st.num -1
)
After that you can update the source table:
UPDATE #SourceTable SET combo = cte.textvalue
FROM #SourceTable st
INNER JOIN
(SELECT id, textvalue,num FROM cte_ConcatenatedText) cte
ON cte.id = st.id and st.num = 1
INNER JOIN
(SELECT id, MAX(num) AS num FROM #SourceTable group by id) maxNum
on maxNum.id = cte.id and maxNum.num = cte.num
SELECT * FROM #SourceTable
Result:
id num textvalue combo
126 1 12 12,58,94
126 2 58 NULL
126 3 94 NULL
130 1 28 28,45,64
130 2 45 NULL
130 3 64 NULL

Related

select query to get result by merging two columns

I have a table like below :
Id Price1 Price2
3 30 20
3 40 20
3 50 20
I want to write a query to get a below result :
Desired Ouput :
RowNo Id Price
1 3 20
2 3 30
3 3 40
4 3 50
Please help!!
Use Cross apply to unpivot the data and generate row number
SELECT Row_number()OVER(ORDER BY price) AS Rowno,*
FROM (SELECT DISTINCT Id,
Price
FROM (VALUES (3,30,20),
(3,40,20),
(3,50,20) ) tc ( Id, Price1, Price2)
CROSS apply (VALUES (Price1),
(Price2)) Cs (Price)) A
Result :
╔═══════╦════╦═══════╗
║ Rowno ║ Id ║ Price ║
╠═══════╬════╬═══════╣
║ 1 ║ 3 ║ 20 ║
║ 2 ║ 3 ║ 30 ║
║ 3 ║ 3 ║ 40 ║
║ 4 ║ 3 ║ 50 ║
╚═══════╩════╩═══════╝
You can use union to combine the rows (so duplicates are removed). And then row_number() to calculate rownum:
select row_number() over (order by price) as rownum, id, price
from ((select id, price1 as price from t) union
(select id, price2 from t
) t
order by price;

SQL Add Running Total Column for each item in a giant table

I wanna calculate a running total based on 5 types of transactions (let's say transaction A, B, C, D, E). But I have over one thousand different products in this table and each product could have millions of transaction records on different days.
So the table looks like this:
ProductID A B C D E Running Total
1 10 0 5 0 5 20
2 15 0 0 0 0 15
3 20 5 0 10 0 35
1 10 0 0 0 0 30 (20 for product 1, plus 10 for product 1 again)
3 12 0 33 0 0 80 (35 for product 3, plus 45 for product 3 again)
The ANSI standard method is to use sum() as a window function:
select t.*,
sum(a + b + c + d + e) over (partition by productid order by <datetimecol>) as RunningTotal
from table t;
SQL tables represent unordered sets, so you need a column that specifies the ordering. I am guessing there is a date/time column somewhere for this purpose.
Most databases support this standard syntax: Oracle, SQL Server 2012+, Postgres, Teradata, and DB2.
Test Data
DECLARE #TABLE TABLE (ProductID INT, A INT, B INT, C INT, D INT, E INT)
INSERT INTO #TABLE VALUES
(1 ,10, 0, 5 , 0 , 5), -- 20
(2 ,15, 0, 0 , 0 , 0), -- 15
(3 ,20, 5, 0 , 10, 0), -- 35
(1 ,10, 0, 0 , 0 , 0), -- 30 (20 for product 1, plus 10 for product 1 again)
(3 ,12, 0, 33, 0 , 0) -- 80
Query
;WITH CTE AS
(
select *
,ROW_NUMBER() OVER (PARTITION BY ProductID ORDER BY ProductID ASC) rn
from #TABLE
)
SELECT ProductID
,A
,B
,C
,D
,E
,runningTotal
FROM CTE c
cross apply (select sum(A+B+C+D+E) as runningTotal
from CTE
where rn <= c.rn
and ProductID = c.ProductID
) as rt
Result
╔═══════════╦════╦═══╦════╦════╦═══╦══════════════╗
║ ProductID ║ A ║ B ║ C ║ D ║ E ║ runningTotal ║
╠═══════════╬════╬═══╬════╬════╬═══╬══════════════╣
║ 1 ║ 10 ║ 0 ║ 5 ║ 0 ║ 5 ║ 20 ║
║ 1 ║ 10 ║ 0 ║ 0 ║ 0 ║ 0 ║ 30 ║
║ 2 ║ 15 ║ 0 ║ 0 ║ 0 ║ 0 ║ 15 ║
║ 3 ║ 20 ║ 5 ║ 0 ║ 10 ║ 0 ║ 35 ║
║ 3 ║ 12 ║ 0 ║ 33 ║ 0 ║ 0 ║ 80 ║
╚═══════════╩════╩═══╩════╩════╩═══╩══════════════╝

How to sort a column based on length of data in it in SQL server

As we all know general sorting is using order by. The sort I want to perform is different. I want the smallest length value in middle of table n the largest ones in top and bottom of it. One half should be descending and another half should be ascending. Can you guys help. It was an interview question.
This is one way:
;WITH CTE AS
(
SELECT *,
RN = ROW_NUMBER() OVER(ORDER BY LEN(YourColumn))
FROM dbo.YourTable
)
SELECT *
FROM CTE
ORDER BY RN%2, (CASE WHEN RN%2 = 0 THEN 1 ELSE -1 END)*RN DESC
Test Data
DECLARE #Table TABLE
(ID INT, Value VARCHAR(10))
INSERT INTO #Table VALUES
(1 , 'A'),
(2 , 'AB'),
(3 , 'ABC'),
(4 , 'ABCD'),
(5 , 'ABCDE'),
(6 , 'ABCDEF'),
(7 , 'ABCDEFG'),
(8 , 'ABCDEFGI'),
(9 , 'ABCDEFGIJ'),
(10 ,'ABCDEFGIJK')
Query
;WITH CTE AS (
SELECT *
,NTILE(2) OVER (ORDER BY LEN(Value) DESC) rn
FROM #Table )
SELECT *
FROM CTE
ORDER BY CASE WHEN rn = 1 THEN LEN(Value) END DESC
,CASE WHEN rn = 2 THEN LEN(Value) END ASC
Result
╔════╦════════════╦════╗
║ ID ║ Value ║ rn ║
╠════╬════════════╬════╣
║ 10 ║ ABCDEFGIJK ║ 1 ║
║ 9 ║ ABCDEFGIJ ║ 1 ║
║ 8 ║ ABCDEFGI ║ 1 ║
║ 7 ║ ABCDEFG ║ 1 ║
║ 6 ║ ABCDEF ║ 1 ║
║ 1 ║ A ║ 2 ║
║ 2 ║ AB ║ 2 ║
║ 3 ║ ABC ║ 2 ║
║ 4 ║ ABCD ║ 2 ║
║ 5 ║ ABCDE ║ 2 ║
╚════╩════════════╩════╝
Here's a short approach that would ge t you started:
WITH cte AS
(
SELECT TOP 1000 number
FROM master..spt_values
WHERE type = 'P' and number >0
)
SELECT number, row_number() OVER(ORDER BY CASE WHEN number %2 = 1 THEN number ELSE -(number) END) pos
FROM cte

How to merger the splited data into same table?

I have a table having data like this
id | Name | Age
---------------
1 | a,b | 10
I want to split that data and merge with the same table like this
id | Name | Age
---------------
1 | a | 10
1 | b | 10
I have a split function.
Test Data
DECLARE #TABLE TABLE(id INT, Name VARCHAR(10), Age INT)
INSERT INTO #TABLE VALUES
(1 ,'a',10), (1 ,'b',10),
(2 ,'d',20), (2 ,'f',20)
Query
SELECT t.ID
,STUFF(( SELECT ',' + Name
FROM #TABLE
WHERE ID = t.ID AND Age = t.Age
FOR XML PATH(''),TYPE)
.value('.','NVARCHAR(MAX)'),1,1,'') AS Name
,t.Age
FROM #TABLE t
GROUP BY t.id, t.Age
Result
╔════╦══════╦═════╗
║ ID ║ Name ║ Age ║
╠════╬══════╬═════╣
║ 1 ║ a,b ║ 10 ║
║ 2 ║ d,f ║ 20 ║
╚════╩══════╩═════╝
It seems to me that OP has the data concatenated and needs to split it. Here's one way to do it (fiddle: http://sqlfiddle.com/#!3/6b2a1/15)
WITH cteSplitCol
AS
(
SELECT id,
Name,
Age,
SUBSTRING(Name, 1, 1) AS Character,
1 AS CharPos,
MAX(LEN(Name)) OVER () AS MaxLen
FROM Table1
UNION ALL
SELECT id,
Name,
Age,
SUBSTRING(Name, CharPos + 1, 1) AS Character,
CharPos + 1,
MaxLen
FROM cteSplitCol
WHERE CharPos + 1 <= MaxLen
)
SELECT id,
Character AS Name,
Age
FROM cteSplitCol
WHERE Character <> ',';

Update duplicate fields in table

I have table with about 100000 records.I need update same fields like this.
For example this is my table
id name
1 sss
2 bbb
3 ccc
4 avg
5 bbb
6 bbb
7 sss
8 mmm
9 avg
After executing script I need get
id name
1 sss
2 bbb
3 ccc
4 avg
5 bbb-5
6 bbb-6
7 sss-7
8 mmm
9 avg-9
How can I do that?
By using CTE
WITH greaterRecord
AS
(
SELECT id,
name,
ROW_NUMBER() OVER(PARTITION BY name ORDER BY id) RN
FROM TableName
)
UPDATE greaterRecord
SET name = name + '-' + CAST(id AS VARCHAR(10))
WHERE RN > 1
SQLFiddle Demo
This is the common query that works on most RDBMS
UPDATE a
SET a.Name = a.Name + '-' + CAST(ID AS VARCHAR(10))
FROM tableName a
LEFT JOIN
(
SELECT MIN(ID) min_ID, name
FROM tableName
GROUP BY name
) b ON a.name = b.name AND
a.ID = b.Min_ID
WHERE b.Name IS NULL
SQLFiddle Demo
OUTPUT after the update statement has been executed
╔════╦═══════╗
║ ID ║ NAME ║
╠════╬═══════╣
║ 1 ║ sss ║
║ 2 ║ bbb ║
║ 3 ║ ccc ║
║ 4 ║ avg ║
║ 5 ║ bbb-5 ║
║ 6 ║ bbb-6 ║
║ 7 ║ sss-7 ║
║ 8 ║ mmm ║
║ 9 ║ avg-9 ║
╚════╩═══════╝
This should do:
;WITH CTE AS
(
SELECT id,
name,
RN = ROW_NUMBER() OVER(PARTITION BY name ORDER BY id)
FROM YourTable
)
UPDATE CTE
SET name = name + '-' + CAST(id AS VARCHAR(8))
WHERE RN > 1