Five Columns to a single row - sql

I have the following data
+--------+
| orders |
+--------+
| S1 |
| S2 |
| S3 |
| S4 |
| S5 |
| S6 |
| S7 |
| S8 |
| S9 |
| S10 |
| S11 |
| S12 |
+--------+
I am required to return the result as follows - fit five rows in one column:
+-----------------+
| Orders |
+-----------------+
| S1,S2,S3,S4,S5 |
| S6,S7,S8,S9,S10 |
| S11,S12 |
+-----------------+
There is nothing to group on or segregate these into rows. So I assigned a row_number and did mod 5 on the row_number. It almost works, but not quite.
Here is what I have tried:
;with mycte as (
select
'S1' as orders
union all select
'S2'
union all select
'S3'
union all select
'S4'
union all select
'S5'
union all select
'S6'
union all select
'S7'
union all select
'S8'
union all select
'S9'
union all select
'S10'
union all select
'S11'
union all select
'S12'
)
,mycte2 as (
Select
orders
,ROW_NUMBER() over( order by orders) %5 as rownum
from mycte
)
select distinct
STUFF((
SELECT ',' + mycte2.orders
FROM mycte2
where t1.rownum= mycte2.rownum
FOR XML PATH('')
), 1, 1, '') orders
, rownum
from mycte2 t1
the result is :
+-----------+--------+
| orders | rownum |
+-----------+--------+
| S1,S3,S8 | 1 |
| S10,S4,S9 | 2 |
| S11,S5 | 3 |
| S12,S6 | 4 |
| S2,S7 | 0 |
+-----------+--------+
Can someone please show me how to get to my desired result?

How about
CREATE TABLE T
([orders] varchar(3));
INSERT INTO T
([orders])
VALUES
('S1'),
('S2'),
('S3'),
('S4'),
('S5'),
('S6'),
('S7'),
('S8'),
('S9'),
('S10'),
('S11'),
('S12');
WITH CTE AS
(
SELECT Orders,
(ROW_NUMBER() OVER(ORDER BY LEN(Orders)) - 1) / 5 RN
FROM T
)
SELECT STRING_AGG(Orders, ',')
FROM CTE
GROUP BY RN
ORDER BY RN;
OR
SELECT STUFF(
(
SELECT ',' + Orders
FROM CTE
WHERE RN = TT.RN
FOR XML PATH('')
), 1, 1, ''
) Orders
FROM CTE TT
GROUP BY RN
ORDER BY RN;
You can use (SELECT 1) instead of LEN(Orders)
Returns:
+-----------------+
| Orders |
+-----------------+
| S1,S2,S3,S4,S5 |
| S6,S7,S8,S9,S10 |
| S11,S12 |
+-----------------+
Demo

Related

how to get individual-clinic-month that are excluded from SQL query

I have the following dataset:
individual | clinic_1 | clinic_2 | month | address_recorded | address_code
1 | A | B | 01-01-2016 | 01-02-1999 | C01
1 | A | A | 01-01-2016 | 01-02-2003 | C02
1 | A | A | 01-01-2016 | 01-02-2001 | C06
1 | A | X | 01-01-2016 | 01-02-2000 | C03
2 | C | B | 01-04-2016 | 01-02-1999 | D04
2 | C | A | 01-04-2016 | 01-02-2001 | D05
2 | C | X | 01-04-2016 | 01-02-2000 | D06
I would like to get:
individual | clinic_1 | month | address_code
1 | A | 01-01-2016 | C02
2 | C | 01-04-2016 | D05
Criteria:
For unique set of individual-clinic_1-month with clinic_1 = clinic_2, select the most
recent date in which address was recorded within clinic_1
For unique set of individual-clinic_1-month with NO instances where clinic_1 = clinic_2,
select the most recent date in which address was recorded across
clinics
I thought about doing:
with cte_1
as
(
select * from table
where clinic_1 = clinic_2
)
,cte_2
as
(
select row_number () over (Partition by clinic_1, individual, month order by clinic_1, individual, month, address_recorded desc) as number, *
from cte_1
)
select individual, clinic_1, month, address_code from cte_2 where number = 1
But I don't know how to get those individual-clinic_1-month for which there are no instances where clinic_1=clinic_2, any ideas?
You can Union two select queries; one to select all records where clinic_1=clinic_2 and another one to select all records where clinic_1<>clinic_2
and clinic_1 not in the results set of the first query.
Both queries are grouped by [individual],[clinic_1], [clinic_2], [mnth] to find all of the required data rows for each [clinic_1] - [mnth] entry. Noting that for the 2nd query [clinic_2] is selected as ''.
Check the following:
with cte as
(SELECT [individual] ,[clinic_1],[clinic_2],[mnth],max([address_recorded]) as m
FROM [MyData] where [clinic_1]=[clinic_2]
group by [individual],[clinic_1],[clinic_2] ,[mnth]
),
cte2 as
(SELECT [MyData].[individual] ,[MyData].[clinic_1],'' as [clinic_2],[MyData].[mnth],max([MyData].[address_recorded]) as m
FROM [MyData]
Left Join cte on cte.individual=MyData.individual
and cte.mnth=MyData.mnth
where [MyData].[clinic_1]<>[MyData].[clinic_2] and cte.individual IS NULL
group by [MyData].[individual],[MyData].[clinic_1], [MyData].[mnth]
),
D as
(SELECT * FROM cte
UNION
SELECT * FROM cte2)
,
LastQr as(
select [MyData].individual, [MyData].clinic_1,[MyData].mnth,[MyData].address_code,
row_number() OVER(PARTITION BY [MyData].individual, [MyData].clinic_1,[MyData].mnth ORDER BY [MyData].individual, [MyData].clinic_1,[MyData].mnth)
as rn from D
INNER JOIN [MyData]
ON D.individual=MyData.individual and D.clinic_1=MyData.clinic_1 and D.mnth=MyData.mnth and D.m=MyData.address_recorded
and (D.clinic_2=MyData.clinic_2 or D.clinic_2='')
)
select * from LastQr where rn=1
See the results from dbfiddle.uk.

SQL combine 2 table and pivot

I don't understand how PIVOT works in SQL. I have 2 tables and I would like to pivot 1 of them in order to get only 1 table with all the data together. I've attached an image with the tables I have and the result that I would like to get.
CREATE TABLE TABLE1
([serie_id] varchar(4), [Maturity] int, [Strategy] int, [Lifetime] varchar(4), [L_max] decimal(10, 5), [W_max] decimal(10, 5), [H_max] decimal(10, 5))
;
INSERT INTO TABLE1
([serie_id], [Maturity], [Strategy], [Lifetime], [L_max], [W_max], [H_max])
VALUES
('id_1', 3, 1, '2', 2.200, 1.400, 1.400),
('id_2', 3, 1, '2', 3.400, 1.800, 2.100),
('id_3', 3, 1, NULL, 24.500, 14.500, 15.000),
('id_4', 3, 1, NULL, 28.000, 24.500, 14.000)
;
CREATE TABLE TABLE2
([serie_id] varchar(4), [L_value] decimal(10, 5), [lrms] decimal(10, 5), [latTmax] decimal(10, 5), [Rdc] decimal(10, 5))
;
INSERT INTO TABLE2
([serie_id], [L_value], [lrms], [latTmax], [Rdc])
VALUES
('id_1', 67.000, 400.000, 400.000, 0.250),
('id_1', 90.000, 330.000, 330.000, 0.350),
('id_1', 120.000, 370.000, 370.000, 0.300),
('id_1', 180.000, 330.000, 300.000, 0.350),
('id_2', 260.000, 300.000, 300.000, 0.400),
('id_2', 360.000, 280.000, 280.000, 0.450),
('id_3', 90.000, 370.000, 370.000, 0.300),
('id_4', 160.000, 340.000, 340.000, 0.400)
;
SQLFiddle
If someone could help me with the SQL query I would appreciate it so much.
In order to get your final result, you are going to have to implement a variety of methods including unpivot, pivot, along with the use of a windowing function like row_number().
Since you have multiple columns in Table2 that need to be pivoted, then you will need to unpivot them first. This is the reverse of pivot, which converts your multiple columns into multiple rows. But before you unpivot, you need some value to identify the values of each row using row_number() - sounds complicated, right?
First, query table2 using the windowing function row_number(). This creates a unique identifier for each row and allows you to easily be able to associate the values for id_1 from any of the others.
select serie_id, l_value, lrms, latTmax, Rdc,
rn = cast(row_number() over(partition by serie_id order by serie_id)
as varchar(10))
from table2;
See Demo. Once you've created this unique identifier, then you will unpivot the L_value, lrms, latTmax, and rdc. You can unpivot the data using several different methods, including the unpivot function, CROSS APPLY, or UNION ALL.
select serie_id,
col, value
from
(
select serie_id, l_value, lrms, latTmax, Rdc,
rn = cast(row_number() over(partition by serie_id order by serie_id)
as varchar(10))
from table2
) d
cross apply
(
select 'L_value_'+rn, L_value union all
select 'lrms_'+rn, lrms union all
select 'latTmax_'+rn, latTmax union all
select 'Rdc_'+rn, Rdc
) c (col, value)
See SQL Fiddle with Demo. The data from table2 is not in a completely different format that can be pivoted into the new columns:
| SERIE_ID | COL | VALUE |
|----------|-----------|-------|
| id_1 | L_value_1 | 67 |
| id_1 | lrms_1 | 400 |
| id_1 | latTmax_1 | 400 |
| id_1 | Rdc_1 | 0.25 |
| id_1 | L_value_2 | 90 |
| id_1 | lrms_2 | 330 |
| id_1 | latTmax_2 | 330 |
| id_1 | Rdc_2 | 0.35 |
The final step would be to PIVOT the data above into the final result:
select serie_id, maturity, strategy, lifetime, l_max, w_max, h_max,
L_value_1, lrms_1, latTmax_1, Rdc_1,
L_value_2, lrms_2, latTmax_2, Rdc_2,
L_value_3, lrms_3, latTmax_3, Rdc_3,
L_value_4, lrms_4, latTmax_4, Rdc_4
from
(
select t1.serie_id, t1.maturity, t1.strategy, t1.lifetime,
t1.l_max, t1.w_max, t1.h_max,
t2.col, t2.value
from table1 t1
inner join
(
select serie_id,
col, value
from
(
select serie_id, l_value, lrms, latTmax, Rdc,
rn = cast(row_number() over(partition by serie_id order by serie_id)
as varchar(10))
from table2
) d
cross apply
(
select 'L_value_'+rn, L_value union all
select 'lrms_'+rn, lrms union all
select 'latTmax_'+rn, latTmax union all
select 'Rdc_'+rn, Rdc
) c (col, value)
) t2
on t1.serie_id = t2.serie_id
) d
pivot
(
max(value)
for col in (L_value_1, lrms_1, latTmax_1, Rdc_1,
L_value_2, lrms_2, latTmax_2, Rdc_2,
L_value_3, lrms_3, latTmax_3, Rdc_3,
L_value_4, lrms_4, latTmax_4, Rdc_4)
) p;
See SQL Fiddle with Demo.
If you have an unknown number of values in Table2 then you will need to use dynamic SQL to create a sql string that will be executed. Converting the above code to dynamic sql is pretty easy once you have the logic correct. The code will be:
DECLARE #cols AS NVARCHAR(MAX),
#query AS NVARCHAR(MAX)
select #cols
= STUFF((SELECT ',' + QUOTENAME(col+cast(rn as varchar(10)))
from
(
select rn = cast(row_number() over(partition by serie_id order by serie_id)
as varchar(10))
from table2
) d
cross apply
(
select 'L_value_', 0 union all
select 'lrms_', 1 union all
select 'latTmax_', 2 union all
select 'Rdc_', 3
) c (col, so)
group by col, rn, so
order by rn, so
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set #query = N'SELECT serie_id, maturity, strategy, lifetime, l_max,
w_max, h_max,' + #cols + N'
from
(
select t1.serie_id, t1.maturity, t1.strategy, t1.lifetime,
t1.l_max, t1.w_max, t1.h_max,
t2.col, t2.value
from table1 t1
inner join
(
select serie_id,
col, value
from
(
select serie_id, l_value, lrms, latTmax, Rdc,
rn = cast(row_number() over(partition by serie_id order by serie_id)
as varchar(10))
from table2
) d
cross apply
(
select ''L_value_''+rn, L_value union all
select ''lrms_''+rn, lrms union all
select ''latTmax_''+rn, latTmax union all
select ''Rdc_''+rn, Rdc
) c (col, value)
) t2
on t1.serie_id = t2.serie_id
) x
pivot
(
max(value)
for col in (' + #cols + N')
) p '
exec sp_executesql #query
See SQL Fiddle with Demo
Both versions will give a result of:
| SERIE_ID | MATURITY | STRATEGY | LIFETIME | L_MAX | W_MAX | H_MAX | L_VALUE_1 | LRMS_1 | LATTMAX_1 | RDC_1 | L_VALUE_2 | LRMS_2 | LATTMAX_2 | RDC_2 | L_VALUE_3 | LRMS_3 | LATTMAX_3 | RDC_3 | L_VALUE_4 | LRMS_4 | LATTMAX_4 | RDC_4 |
|----------|----------|----------|----------|-------|-------|-------|-----------|--------|-----------|-------|-----------|--------|-----------|--------|-----------|--------|-----------|--------|-----------|--------|-----------|--------|
| id_1 | 3 | 1 | 2 | 2.2 | 1.4 | 1.4 | 67 | 400 | 400 | 0.25 | 90 | 330 | 330 | 0.35 | 120 | 370 | 370 | 0.3 | 180 | 330 | 300 | 0.35 |
| id_2 | 3 | 1 | 2 | 3.4 | 1.8 | 2.1 | 260 | 300 | 300 | 0.4 | 360 | 280 | 280 | 0.45 | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) |
| id_3 | 3 | 1 | (null) | 24.5 | 14.5 | 15 | 90 | 370 | 370 | 0.3 | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) |
| id_4 | 3 | 1 | (null) | 28 | 24.5 | 14 | 160 | 340 | 340 | 0.4 | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) | (null) |

Select rows with maximum value where values in two columns are same

I have a simple table like this
....................................
| hotelNo | roomType | totalBooking |
....................................
| 1 | single | 2 |
| 1 | family | 4 |
| 2 | single | 3 |
| 2 | family | 2 |
| 3 | single | 1 |
.....................................
Now I want to get the most commonly booked roomType for each hotels, i.e the following result
......................
| hotelNo | roomType |
......................
| 1 | family |
| 2 | single |
| 3 | single |
......................
P.S I use sub-query to get the first table
If you want the maximum, you can use window functions:
select hotelNo, roomType
from (select t.*, row_number() over (partition by hotelNo order by totalBooking desc) as seqnum
from table t
) t
where seqnum = 1;
Sample table
SELECT * INTO #TEMP
FROM
(
SELECT 1 HOTELNO ,'SINGLE' ROOMTYPE ,2 TOTALBOOKING
UNION ALL
SELECT 1,'FAMILY',4
UNION ALL
SELECT 2,'SINGLE',3
UNION ALL
SELECT 2,'FAMILY',2
UNION ALL
SELECT 3,'SINGLE',1
)TAB
Result query
;WITH CTE1 AS
(
SELECT HOTELNO,ROOMTYPE,TOTALBOOKING,
MAX(TOTALBOOKING) OVER (PARTITION BY HOTELNO) MAXX
FROM #TEMP
)
SELECT DISTINCT HOTELNO,ROOMTYPE,MAXX
FROM CTE1
WHERE TOTALBOOKING=MAXX
Another way of doing is by using Aggregate Function Max
SELECT A.hotelNo,
A.roomType,
A.totalBooking
FROM tablename A
JOIN (SELECT Max (totalBooking) totalBooking,
hotelNo
FROM tablename
group by hotelNo) B
ON a.totalBooking = b.totalBooking
AND a.hotelNo = b.hotelNo
SQL FIDDLE DEMO

SELECT only latest record of an ID from given rows

I have this table shown below...How do I select only the latest data of the id based on changeno?
+----+--------------+------------+--------+
| id | data | changeno | |
+----+--------------+------------+--------+
| 1 | Yes | 1 | |
| 2 | Yes | 2 | |
| 2 | Maybe | 3 | |
| 3 | Yes | 4 | |
| 3 | Yes | 5 | |
| 3 | No | 6 | |
| 4 | No | 7 | |
| 5 | Maybe | 8 | |
| 5 | Yes | 9 | |
+----+---------+------------+-------------+
I would want this result...
+----+--------------+------------+--------+
| id | data | changeno | |
+----+--------------+------------+--------+
| 1 | Yes | 1 | |
| 2 | Maybe | 3 | |
| 3 | No | 6 | |
| 4 | No | 7 | |
| 5 | Yes | 9 | |
+----+---------+------------+-------------+
I currently have this SQL statement...
SELECT id, data, MAX(changeno) as changeno FROM Table1 GROUP BY id;
and clearly it doesn't return what I want. This should return an error because of the aggrerate function. If I added fields under the GROUP BY clause it works but it doesn't return what I want. The SQL statement is by far the closest I could think of. I'd appreciate it if anybody could help me on this. Thank you in advance :)
This is typically referred to as the "greatest-n-per-group" problem. One way to solve this in SQL Server 2005 and higher is to use a CTE with a calculated ROW_NUMBER() based on the grouping of the id column, and sorting those by largest changeno first:
;WITH cte AS
(
SELECT id, data, changeno,
rn = ROW_NUMBER() OVER (PARTITION BY id ORDER BY changeno DESC)
FROM dbo.Table1
)
SELECT id, data, changeno
FROM cte
WHERE rn = 1
ORDER BY id;
You want to use row_number() for this:
select id, data, changeno
from (SELECT t.*,
row_number() over (partition by id order by changeno desc) as seqnum
FROM Table1 t
) t
where seqnum = 1;
Not a well formed or performance optimized query but for small tasks it works fine.
SELECT * FROM TEST
WHERE changeno IN (SELECT MAX(changeno)
FROM TEST
GROUP BY id)
for other alternatives :
DECLARE #Table1 TABLE
(
id INT, data VARCHAR(5), changeno INT
);
INSERT INTO #Table1
SELECT 1,'Yes',1
UNION ALL
SELECT 2,'Yes',2
UNION ALL
SELECT 2,'Maybe',3
UNION ALL
SELECT 3,'Yes',4
UNION ALL
SELECT 3,'Yes',5
UNION ALL
SELECT 3,'No',6
UNION ALL
SELECT 4,'No',7
UNION ALL
SELECT 5,'Maybe',8
UNION ALL
SELECT 5,'Yes',9
SELECT Y.id, Y.data, Y.changeno
FROM #Table1 Y
INNER JOIN (
SELECT id, changeno = MAX(changeno)
FROM #Table1
GROUP BY id
) X ON X.id = Y.id
WHERE X.changeno = Y.changeno
ORDER BY Y.id

Dynamically create ranges from numeric sequences

I have a table like the following:
+----+-----+-----+
| ID | GRP | NR |
+----+-----+-----+
| 1 | 1 | 101 |
| 2 | 1 | 102 |
| 3 | 1 | 103 |
| 4 | 1 | 105 |
| 5 | 1-2 | 106 |
| 6 | 1-2 | 109 |
| 7 | 1-2 | 110 |
| 8 | 2 | 201 |
| 9 | 2 | 202 |
| 10 | 3 | 300 |
| 11 | 3 | 350 |
| 12 | 3 | 351 |
| 13 | 3 | 352 |
+----+-----+-----+
I wanted to create a view which groups this list by GRP and concatenates values in NR.
Is it possible to dynamically detect sequences and shorten them into ranges?
Like 1, 2, 3, 5 would become 1-3, 5.
So the result should look like this:
+-----+--------------------+
| GRP | NRS |
+-----+--------------------+
| 1 | 101 - 103, 105 |
| 1-2 | 106, 109 - 110 |
| 2 | 201 - 202 |
| 3 | 300, 350 - 352 |
+-----+--------------------+
What i got now is simply concatenate values, so the table above would become this:
+-----+--------------------+
| GRP | NRS |
+-----+--------------------+
| 1 | 101, 102, 103, 105 |
| 1-2 | 106, 109, 110 |
| 2 | 201, 202 |
| 3 | 300, 350, 351, 352 |
+-----+--------------------+
Here's the actual statement:
DECLARE #T TABLE
(
ID INT IDENTITY(1, 1)
, GRP VARCHAR(10)
, NR INT
)
INSERT INTO #T
VALUES ('1',101),('1',102),('1',103),('1',105)
,('1-2',106),('1-2',109), ('1-2',110)
,('2',201),('2',202)
,('3',300),('3',350),('3',351),('3',352)
SELECT * FROM #T
;WITH GROUPNUMS (RN, GRP, NR, NRS) AS
(
SELECT 1, GRP, MIN(NR), CAST(MIN(NR) AS VARCHAR(MAX))
FROM #T
GROUP BY GRP
UNION ALL
SELECT CT.RN + 1, T.GRP, T.NR, CT.NRS + ', ' + CAST(T.NR AS VARCHAR(MAX))
FROM #T T
INNER JOIN GROUPNUMS CT ON CT.GRP = T.GRP
WHERE T.NR > CT.NR
)
SELECT NRS.GRP, NRS.NRS
FROM GROUPNUMS NRS
INNER JOIN (
SELECT GRP, MAX(RN) AS MRN
FROM GROUPNUMS
GROUP BY GRP
) R
ON NRS.RN = R.MRN AND NRS.GRP = R.GRP
ORDER BY NRS.GRP
Can anyone tell me if it's easily possible to do something like that?
Would be great if anyone has an idea and would like to share it.
SQLFiddle demo
with TRes
as
(
select T.GRP,T.NR NR,
CASE WHEN T1.NR IS NULL and T2.NR is null
THEN CAST(T.NR as VARCHAR(MAX))
WHEN T1.NR IS NULL and T2.NR IS NOT NULL
THEN '-'+CAST(T.NR as VARCHAR(MAX))
WHEN T1.NR IS NOT NULL and T2.NR IS NULL
THEN CAST(T.NR as VARCHAR(MAX))+'-'
END AS NR_GRP
from T
left join T T1 on T.Grp=T1.Grp and t.Nr+1=t1.Nr
left join T T2 on T.Grp=T2.Grp and t.Nr-1=t2.Nr
WHERE T1.NR IS NULL or T2.NR IS NULL
)
SELECT
GRP,
REPLACE(
substring((SELECT ( ',' + NR_GRP)
FROM TRes t2
WHERE t1.GRP = t2.GRP
ORDER BY
GRP,
NR
FOR XML PATH( '' )
), 2, 10000 )
,'-,-','-')
FROM TRes t1
GROUP BY GRP
Please check my try:
DECLARE #T TABLE
(
ID INT IDENTITY(1, 1)
, GRP VARCHAR(10)
, NR INT
)
INSERT INTO #T
VALUES ('1',101),('1',102),('1',103),('1',105)
,('1-2',106),('1-2',109), ('1-2',110)
,('2',201),('2',202)
,('3',300),('3',350),('3',351),('3',352)
SELECT * FROM #T
;WITH T1 as
(
SELECT GRP, NR, ROW_NUMBER() over(order by GRP, NR) ID FROM #T
)
,T as (
SELECT *, 1 CNT FROM T1 where ID=1
union all
SELECT b.*, (case when T.NR+1=b.NR and T.GRP=b.GRP then t.CNT
else T.CNT+1 end)
from T1 b INNER JOIN T on b.ID=T.ID+1
)
, TN as(
select *,
MIN(NR) over(partition by GRP, CNT) MinVal,
MAX(NR) over(partition by GRP, CNT) MaxVal
From T
)
SELECT GRP, STUFF(
(SELECT distinct ','+(CASE WHEN MinVal=MaxVal THEN CAST(MinVal as nvarchar(10)) ELSE CAST(MinVal as nvarchar(10))+'-'+cast(MaxVal as nvarchar(10)) END)
FROM TN b where b.GRP=a.GRP
FOR XML PATH(''),type).value('.','nvarchar(max)'),1,1,'') AS [ACCOUNT NAMES]
FROM TN a GROUP BY GRP