Split column value to a new row - sql

I have a SQL Server table like this:
id
description
items
123
Women clothing
T-shirt & Bottom & Top
124
sports items
badminton racket
125
gadgets
iphone & airpod & charging
I want to split the column items value to a new row for each one item like this:
id
description
items
123
Women clothing
T-shirt
123
Women clothing
Bottom
123
Women clothing
Top
124
sports items
badminton racket
125
gadgets
iphone
125
gadgets
airpod
125
gadgets
charging

To create function check this screenshot. select your db and execute following:
If you dont have string_split func. (below sqlserver2016) you can use this function , its working in our production enviroment for years :
USE xxx
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER FUNCTION [dbo].[StringSplit]
(#pString VARCHAR(8000), #pDelimiter CHAR(1))
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
),
E2(N) AS (SELECT 1 FROM E1 a, E1 b),
E4(N) AS (SELECT 1 FROM E2 a, E2 b),
cteTally(N) AS (
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
;
and query :
select * from
(SELECT 123 id, 'T-shirt & Bottom & Top' T UNION ALL
SELECT 124 , 'badminton racket'UNION ALL
SELECT 125 , 'iphone & airpod & charging' ) x
CROSS APPLY dbo.StringSplit(x.T, '&')

WITH CTE(id ,description,items) AS
(
SELECT 123, 'Women clothing' , 'T-shirt & Bottom & Top' UNION ALL
SELECT 124 ,'sports items' ,'badminton racket'UNION ALL
SELECT 125 ,'gadgets' ,'iphone & airpod & charging'
)
SELECT C.ID, C.DESCRIPTION, TRIM(S.VALUE)
FROM CTE AS C
CROSS APPLY STRING_SPLIT(C.items, '&') S;

you can use following SQL server code easily:
select Tbl.RowID,
Tbl.ColMain,
[value] ExtractedValue from
(
SELECT 123 as RowID, 'T-shirt & Bottom & Top' As ColMain UNION ALL
SELECT 124 , 'badminton racket' UNION ALL
SELECT 125 , 'iphone & airpod & charging'
) Tbl
CROSS APPLY String_Split(Tbl.ColMain, '&')

Related

SQL Query - Looping

I'm trying to output a record per part for each the quantity in the field. E.g. if a part has a qty of 10 then I'd want that part to be listed 10 times, if the qty was 2 then I'd only want the part to be list twice.
Here's a sample of the data:
Part Qty
PSR6621581 17
PSR6620952 13
PSR6620754 11
PSR6621436 11
PSR6621029 9
PSR661712 9
PSR661907 9
PSR662998 8
PSR6620574 7
PSR661781 7
Any suggestions?
You can use a recursive CTE to expand the rows. For example:
with
p as (
select part, qty, 1 as n from t
union all
select part, qty, n + 1
from p
where n < qty
)
select part, qty from p
Result:
part qty
----- ---
ABC 1
DEF 4
DEF 4
DEF 4
DEF 4
See running example at db<>fiddle.
Here is another option. This is using a tally which is the ideal way to handle this type of thing. I keep this view on my system as a crazy fast way of having a tally table.
create View [dbo].[cteTally] as
WITH
E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
select N from cteTally
GO
Now we just create a dummy table with your sample data.
declare #Something table
(
Part varchar(10)
, Qty int
)
insert #Something
select 'PSR6621581', 17 union all
select 'PSR6620952', 13 union all
select 'PSR6620754', 11 union all
select 'PSR6621436', 11 union all
select 'PSR6621029', 9 union all
select 'PSR661712', 9 union all
select 'PSR661907', 9 union all
select 'PSR662998', 8 union all
select 'PSR6620574', 7 union all
select 'PSR661781', 7
Now that the setup is complete the query to produce the output you want is super easy and lightning fast to execute.
select s.Part
, s.Qty
from #Something s
join cteTally t on t.N <= s.Qty
order by s.Part
, t.N

TSQL, Get top N unique rows across ordered groups

I have the following table of values, sorted by arbitrary segment id specified by the user. ( I know how to do that query and below are the results )
SegmentID SequenceID
3 100
3 200
3 400
3 430
1 100
1 200
1 300
1 410
2 100
2 200
2 300
2 420
I need a SQL query ( Sql Server 2012 ) that returns top N Records in order of Precedence where SequenceID is not repeated.
Example: user wants 7 sequences in order of segment preference: 3, 1,2.
The correct answer is
SegmentID SequenceID
3 100
3 200
3 400
3 430
1 300
1 410
2 420
in a nutshell, i need to traverse recordset from top to bottom, grab unique sequences as i go and add to the list.
How can I do that in a TSql statement?
create table #data (SegmentID int,SequenceID int);
insert into #data values
(3,100),
(3,200),
(3,400),
(3,430),
(1,100),
(1,200),
(1,300),
(1,410),
(2,100),
(2,200),
(2,300),
(2,420);
This table declares the ordering preference:
create table #prefs (Preference int, SegmentID int);
insert into #prefs values(1,3),(2,1),(3,2);
with cte as
(
select #data.SegmentID,
#data.SequenceID,
Preference,
row_number() over (partition by SequenceID order by Preference) rn
from #data
inner join #prefs on #data.SegmentID = #prefs.SegmentID
)
select SegmentId,
SequenceID
from cte
where rn = 1
order by Preference, SequenceID;
DEMO:
http://rextester.com/JKNKD15000
With cte (SegmentID, SequenceID) as
(SELECT 3, 100 UNION ALL
SELECT 3, 200 UNION ALL
SELECT 3, 400 UNION ALL
SELECT 3, 430 UNION ALL
SELECT 1, 100 UNION ALL
SELECT 1, 200 UNION ALL
SELECT 1, 300 UNION ALL
SELECT 1, 410 UNION ALL
SELECT 2, 100 UNION ALL
SELECT 2, 200 UNION ALL
SELECT 2, 300 UNION ALL
SELECT 2, 420),
userOrder (SegmentID, orderID) as (
SELECT 3, 1 UNION ALL
SELECT 1, 2 UNION ALL
SELECT 2, 3),
Results (SegmentID, SequenceID, RN, orderID) as (
Select A.*
, Row_number() over (Partition by A.SequenceID order by B.orderID) RN
, B.orderID
from cte A
INNER JOIN userOrder B
on A.SegmentID = B.SegmentID)
Select Top 7 *
from results where RN = 1
order by OrderID, SequenceID

adding a value to a column from data in next row sql

Base Table
id line_number
1 1232
2 1456
3 1832
4 2002
I wish to add values to a new table such that the next row's value becomes the value in a new column with the last row's value being same..
The final output I need to produce is:
id line_number end_line_number
1 1232 1456
2 1456 1832
3 1832 2002
4 2002 2002
The database is sql server.
Any help is sincerely appreciated.
Thanks
After SQL Server 2012, you can use LEAD like this.
;WITH BaseTable as
(
SELECT 1 id, 1232 line_number
UNION ALL SELECT 2 , 1456
UNION ALL SELECT 3, 1832
UNION ALL SELECT 4 , 2002
)
SELECT id,line_number,(LEAD(line_number,1,line_number) OVER(ORDER BY id ASC))
FROM BaseTable
For previous versions, try this
;WITH BaseTable as
(
SELECT 1 id, 1232 line_number
UNION ALL SELECT 2 , 1456
UNION ALL SELECT 3, 1832
UNION ALL SELECT 4 , 2002
), OrderedBaseTable as
(
SELECT id,line_number,ROW_NUMBER() OVER(ORDER BY id asc) rw
FROM BaseTable
)
SELECT t1.id,t1.line_number,ISNULL(t2.line_number,t1.line_number) next_line_number
FROM OrderedBaseTable t1
LEFT JOIN OrderedBaseTable t2
ON t1.rw = t2.rw - 1
Try this
With T as (
Select id, line_number, Row_Number() OVER(Order By id) + 1 As TempId From TableName)
Select T1.id, T1.line_number, ISNULL(T2.line_number,T1.line_number) As end_line_number From T T1
Left Join T T2 on T2.id = T1.TempId
SQL Fiddle Demo

Split string and select into new table

I have a table with a structure like this
ID pointCount pointSeries
1 282 35.1079,-111.0151,35.1088,-111.0196...
Obviously the point series is string has pair of lat lon as points. I want to select the pointSeries into a new table
ID Lat Lon
1 35.1079 -111.0151
1 35.1088 -111.0196
What's the best way I can do a split and select into query?
You need to have a function for splitting comma-delimited strings into separate rows. Here is the DelimitedSplit8K function by Jeff Moden.
CREATE FUNCTION [dbo].[DelimitedSplit8K](
#pString NVARCHAR(4000), #pDelimiter NCHAR(1)
)
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
)
,E2(N) AS (SELECT 1 FROM E1 a, E1 b)
,E4(N) AS (SELECT 1 FROM E2 a, E2 b)
,cteTally(N) AS(
SELECT TOP (ISNULL(DATALENGTH(#pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)
,cteStart(N1) AS(
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(#pString,t.N,1) = #pDelimiter
),
cteLen(N1,L1) AS(
SELECT
s.N1,
ISNULL(NULLIF(CHARINDEX(#pDelimiter,#pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
SELECT
ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(#pString, l.N1, l.L1)
FROM cteLen l
Then you need to pivot the result of the split to achieve the desired result:
;WITH CteSplitted AS(
SELECT
t.ID,
x.ItemNumber,
Item = CAST(x.Item AS NUMERIC(16,4)),
RN = (ROW_NUMBER() OVER(PARTITION BY ID ORDER BY ItemNumber) + 1) / 2
FROM Test t
CROSS APPLY dbo.DelimitedSplit8K(t.PointSeries, ',') x
)
SELECT
ID,
Lat = MAX(CASE WHEN ItemNumber % 2 = 1 THEN Item END),
Lon = MAX(CASE WHEN ItemNumber % 2 = 0 THEN Item END)
FROM CteSplitted
GROUP BY ID, RN

How to display duplicate items based on the quantity in SQL?

I have a table with the field content :
item qty
----- -----
tea 2
I want to display as below
item qty
---- ------
tea 1
tea 1
How to create SQL query like above ?
SAMPLE TABLE
CREATE TABLE #TEMP(ITEM VARCHAR(10),QTY INT)
INSERT INTO #TEMP
SELECT 'TEA',2
UNION ALL
SELECT 'COFFEE',3
If you have more than 1 type of item and you want to get the number of items, you can do with the following query.
QUERY
;WITH CTE AS
(
-- You will get each ITEM from your table
SELECT ITEM, QTY, 1 NEWQTY
FROM #TEMP
UNION ALL
-- Here it will loop and shows the repeated values of each ITEM
SELECT C1.ITEM,C1.QTY,NEWQTY + 1
FROM CTE C1
JOIN #TEMP T1 ON C1.item= T1.ITEM
WHERE C1.NEWQTY < T1.qty
)
SELECT ITEM,
1 QTY
FROM CTE
ORDER BY ITEM
Click here to view result
Use Recursive CTE
;WITH cte
AS (SELECT item,qty,1 num from yourtable
UNION ALL
SELECT item,
qty,
num + 1
FROM cte
WHERE num < qty)
SELECT item,
1 as Qty
FROM cte
or use Tally table. It will have a better performance when compared to Recursive CTE
;WITH e1(n) AS
(
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), -- 10
e2(n)
AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b) -- 10*10
--e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100
SELECT a.item,
1 AS qty
FROM yourtable a
JOIN (SELECT n = Row_number()OVER (ORDER BY n)
FROM e2) b
ON b.n <= a.qty;
Note : Based on the Quantity you may have to increase the cross join of CTE's
Check here for more info