Splitting Comma Separated string in TSQL and converting result into rows - sql-server

Can some one help me in this case? I have this table input
ID
BuildingName
Rooms
Details
Contact
1
BurjKhalifa
2,4-9,12
RoomsAvailable
+971-12-0000
and I want this output
ID
BuildingName
Rooms
Details
Contact
1
BurjKhalifa
2
RoomsAvailable
+971-12-0000
1
BurjKhalifa
4
RoomsAvailable
+971-12-0000
1
BurjKhalifa
5
RoomsAvailable
+971-12-0000
1
BurjKhalifa
6
RoomsAvailable
+971-12-0000
1
BurjKhalifa
7
RoomsAvailable
+971-12-0000
1
BurjKhalifa
8
RoomsAvailable
+971-12-0000
1
BurjKhalifa
9
RoomsAvailable
+971-12-0000
1
BurjKhalifa
12
RoomsAvailable
+971-12-0000

With the aid of a CROSS APPLY (or two) and an ad-hoc tally/numbers table
Example
Select A.ID
,A.BuildingName
,Rooms = C.N
,A.Details
,A.Contact
From YourTable A
Cross Apply (
Select R1=min(try_convert(int,B2.value))
,R2=max(try_convert(int,B2.value))
From string_split(Rooms,',') B1
Cross Apply string_split(Value,'-') B2
Group By B1.value
)B
Join ( Select Top 500 N=-1+Row_Number() Over (Order By (Select NULL)) From master..spt_values n1, master..spt_values n2 ) C
on N between R1 and R2
Order by ID,C.N
Results

Related

How to order by one column, but rank based on a different column that is not numeric?

I have four columns that I am trying to rank. They need to be grouped by employee ID and then listed low to high by order number. Then when everything is in order, I'm really trying to get the ranking of where the city falls in that order. If the same city is listed after another for the same employee then I want that those ranked the same.
An example of the table is below. The order is correct, but the ranking is not for what I'm trying to do.
Name Employee_ID Order_Number City Rank
John 1 1 Boston 1
John 1 2 Boston 2
Will 2 1 Peabody 1
Will 2 2 Weston 2
Will 2 3 Newton 3
select Name, Employee_ID, Order_Number, City,
dense_rank() over(partition by Employee_ID order by Order_Number) as rank
from #Employee
How I would actually want the results are:
Name Employee_ID Order_Number City Rank
John 1 1 Boston 1
John 1 2 Boston 1
Will 2 1 Boston 1
Will 2 2 Weston 2
Will 2 3 Newton 3
Then I would eventually remove the duplicate Cities to end up with:
Name Employee_ID Order_Number City Rank
John 1 1 Boston 1
Will 2 1 Boston 1
Will 2 2 Weston 2
Will 2 3 Newton 3
You can try this following script to get your desired output.
SELECT Name, Employee_ID, Order_Number, City ,
ROW_NUMBER() OVER (PARTITION BY Employee_ID ORDER BY Order_Number) rank
(
select Name, Employee_ID, Order_Number, City,
dense_rank() over(partition by Employee_ID,city order by Order_Number) as rank
from #Employee
)A
WHERE rank = 1
Output from your result set is-
Name Employee_ID Order_Number City rank
John 1 1 Boston 1
Will 2 1 Peabody 1
Will 2 2 Weston 2
Will 2 3 Newton 3
Check output of the script on Fiddle.
You can use LAG() to check if the previous city is the same. If the previous city is different or null then we take rank as it is, if cities are same then rank - 1 gives us the same number as row above. Demo
with cte as (select Name, Employee_ID, Order_Number, City,
dense_rank() over (partition by Employee_ID order by Order_Number) as rank,
lag(City) over (partition by Employee_ID order by Order_Number) as previousCity
from #Employee)
select
Name, Employee_ID, Order_Number, City,
case when previousCity = city then rank - 1
else rank end as rank
from cte

Using a CTE to provide a cumulative total

I have a table with some forenames in:
SELECT * FROM d;
Forename
--------------------------------
Robert
Susan
Frances
Kate
May
Alex
Anna
I want to pull a cumulative total of name lengths alphabetically. So far I have:
WITH Names ( RowNum, Forename, ForenameLength )
AS ( SELECT ROW_NUMBER() OVER ( ORDER BY forename ) AS RowNum ,
Forename ,
LEN(forename) AS ForenameLength
FROM d
)
SELECT RowNum ,
Forename ,
ForenameLength ,
ISNULL(ForenameLength + ( SELECT ISNULL(SUM(ForenameLength),0)
FROM Names
WHERE RowNum < n.RowNum
), 0) AS CumLen
FROM NAMES n;
RowNum Forename ForenameLength CumLen
-------------------- -------------------------------- -------------- -----------
1 Alex 4 4
2 Anna 4 8
3 Frances 7 15
4 Kate 4 19
5 May 3 22
6 Robert 6 28
7 Susan 5 33
But I understand that it should be possible to do this (recursively) within the CTE. Anyone know how this could be achieved?
N.B. whilst we are developing on 2012, the current live system is 2008 so any solution would need to be backwards compatible at least in the short term.
You are on SQL Server 2012 and should use sum() over() instead.
select row_number() over(order by d.Forename) as RowNum,
d.Forename,
len(d.Forename) as ForenameLength,
sum(len(d.Forename)) over(order by d.Forename rows unbounded preceding) as CumLen
from d
order by d.Forename;
Result:
RowNum Forename ForenameLength CumLen
-------- ------------ -------------- -----------
1 Alex 4 4
2 Anna 4 8
3 Frances 7 15
4 Kate 4 19
5 May 3 22
6 Robert 6 28
7 Susan 5 33
Update:
If you for some reason absolutely want a recursive version it could look something like this:
with C as
(
select top(1)
1 as RowNum,
d.Forename,
len(d.Forename) as ForenameLength,
len(d.Forename) as CumLen
from d
order by d.Forename
union all
select d.RowNum,
d.Forename,
d.ForenameLength,
d.CumLen
from (
select C.RowNum + 1 as RowNum,
d.Forename,
len(d.Forename) as ForenameLength,
C.CumLen + len(d.Forename) as CumLen,
row_number() over(order by d.ForeName) as rn
from d
inner join C
on C.Forename < d.Forename
) as d
where d.rn = 1
)
select C.RowNum,
C.Forename,
C.ForenameLength,
C.CumLen
from C;
Adapted from Performance Tuning the Whole Query Plan by Paul White.

Select top N with joins

I am looking to join 2 tables with top n results of other table as explained below.
OrderHeader
OH_Id OrderDate
----------------------
1 2014-06-01
2 2014-06-02
3 2014-06-03
4 2014-06-04
5 2014-06-05
OrderProducts
OP_Id OH_Id Quantity
------------------------------
1 1 1
2 1 2
3 2 1
4 3 3
5 4 4
6 4 1
7 4 2
8 5 2
9 5 1
I am expecting result something like this for top 3 orders (4 rows).
OH_Id OrderDate Op_Id Quantity
------------------------------------------------
1 2014-06-01 1 1
1 2014-06-01 2 2
2 2014-06-02 3 1
3 2014-06-03 4 3
Note: I am looking specifically to join 2 tables rather writing as SP or looped queries.
select top 3 o.oh_id, o.orderdate, oo.op_id, oo.quantity
from orderheader o
join orderproducts oo on o.oh_id = oo.oh_id
If you want the first 3 order numbers from OrderHeader with all corresponding rows from OrderProducts try this.
select o.oh_id
,o.orderdate
,oo.op_id
,oo.quantity
from (SELECT TOP 3 *
FROM orderheader
ORDER BY OH_ID --or Date etc...
) o
INNER JOIN orderproducts oo
on o.oh_id = oo.oh_id
I think your description is confusing. You don't want top 3 as that will return only 3 rows. You just want ids 1-3 from what it sounds like.
SELECT *
FROM OrderHeader a
JOIN OrderHeader b on a.oh_id = b.oh_id
WHERE a.oh_id <= 3
you have to use a sub query like this
SELECT * FROM OrderHeader
INNER JOIN OrderProducts ON OrderHeader.OH_Id = OrderProducts.OH_Id
WHERE OrderHeader.OH_Id IN (SELECT TOP 3 OH_Id FROM OrderHeader)
A test sql fiddle is here
hope this helps

Applying grouped ranking using ROW_NUMBER

I m Looking for ways to assign the row numbers as below for the table
Roll No Name Score
1 ABC 10
1 ABC 10
1 DEF 8
2 ASC 9
2 YHN 4
3 IOP 5
3 YHN 4
I m looking for a way to assign the roll no as Rownumber()
Roll No Name Score Row_Number
1 ABC 10 1
1 ABC 10 2
1 DEF 8 3
2 ASC 9 1
2 YHN 4 2
3 IOP 5 1
3 YHN 4 2
I m trying to work around with Row_number() , it is isnt working . ANy inputs on this world be great :)
Thanks !!!!
SELECT [Roll No], Name, Score, [ROW_NUMBER] =
ROW_NUMBER() OVER (PARTITION BY [Roll No] ORDER BY Score DESC)
FROM dbo.table
ORDER BY [Roll No], [ROW_NUMBER];
If you later decide that you want to handle ties in a different way, play with using RANK() or DENSE_RANK() in place of ROW_NUMBER()...

Select Distinct Sum SQL Server

I have a problem with the SUM(tblReview.GradePoint) I get 6 as result for GroupID Ballon because I have three products with GroupID Ballon, but I want the result to be 2 because there is only one review with that groupID in tblReview, how can I do that?
SELECT Product.GroupID,
max(Product.ProductID) as ProductID,
max (Product.BrandID) as BrandID,
max (Product.Year) as Year,
max (Product.Name) as Name,
max (tblBrand.BrandName)as BrandName,
SUM(tblReview.GradePoint) as GradePoint
FROM Product INNER JOIN
tblBrand ON Product.BrandID = tblBrand.BrandID LEFT OUTER JOIN
tblReview ON Product.GroupID = tblReview.GroupID
GROUP BY Product.GroupID
HAVING COUNT(distinct Product.GroupID) = 1
ORDER BY GradePoint DESC
Product
ProductID GroupID BrandID
--------------------------------------
1 Ballon 10
2 Ballon 10
3 Ballon 10
4 Ball 21
5 Ball 21
6 Chess 2
7 Chess 2
8 Hat 30
tblReview
ProductID GroupID GradePoint
------------------------------------------
2 Ballon 2
4 Ball 1
5 Ball 1
5 Ball 1
6 Chess -1
8 Hat 1
tblBrand
BrandID ProductID
-----------------------
10 1
10 2
10 3
21 4
21 5
2 6
2 7
30 8
Try this:
SELECT Product.GroupID,
max(Product.ProductID) as ProductID,
max (Product.BrandID) as BrandID,
max (Product.Year) as Year,
max (Product.Name) as Name,
max (tblBrand.BrandName)as BrandName,
max(tblReview.GradePoint) as GradePoint
FROM Product INNER JOIN
tblBrand ON Product.BrandID = tblBrand.BrandID LEFT OUTER JOIN
(SELECT GroupID, SUM(GradePoint) GradePoint FROM tblReview GROUP BY GroupID) tblReview ON Product.GroupID = tblReview.GroupID
GROUP BY Product.GroupID

Resources