Dynamically create ranges from numeric sequences - sql

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

Related

How to show values that has a suitable pair in sql server

I have a table like this
---------------------------------------------
Id | TransactionId | Amount | Account| crdr |
---------------------------------------------
1 | 1 | 100 | 11111 | 1 |
2 | 2 | 130 | 13133 | 1 |
3 | 1 | 100 | 12111 | 2 |
4 | 2 | 130 | 13233 | 2 |
5 | 2 | 110 | 12122 | 1 |
What I need to display is, show these records as pairs (I have grouped them by transactionid, Amount).
SELECT TransactionId ,Amount , Account, CrDr
FROM Table1 ORDER BY TransactionId ASC,Amount ASC, CrDr ASC
But I want to ignore the records which dont have a pair, as a Example for this above records set result should be like this
---------------------------------------------
TransactionId | Amount | Account| crdr |
---------------------------------------------
1 | 100 | 11111 | 1 |
1 | 100 | 12111 | 2 |
2 | 130 | 13133 | 1 |
2 | 130 | 13233 | 2 |
Can someone suggest a solution for this.
You could use a correlated subquery with a NOT EXISTS condition to ensure that another record exists with the same TransactionId and Amount:
SELECT TransactionId ,Amount , Account, CrDr
FROM Table1 t
WHERE EXISTS (
SELECT 1
FROM Table1 t1
WHERE
t.id <> t1.id
AND t.TransactionId = t1.TransactionId
AND t.Amount = t1.Amount
)
ORDER BY TransactionId ASC,Amount ASC, CrDr ASC
Demo on DB Fiddle:
TransactionId | Amount | Account | CrDr
------------: | -----: | ------: | ---:
1 | 100 | 11111 | 1
1 | 100 | 12111 | 2
2 | 130 | 13133 | 1
2 | 130 | 13233 | 2
Try this:
DECLARE #DataSource TABLE
(
[Id] INT
,[TransactionId] INT
,[Amount] INT
,[Account] INT
,[crdr] INT
);
INSERT INTO #DataSource ([Id], [TransactionId], [Amount], [Account], [crdr])
VALUES (1, 1, 100, 11111, 1)
,(2, 2, 130, 13133, 1)
,(3, 1, 100, 12111, 2)
,(4, 2, 130, 13233, 2)
,(5, 2, 110, 12122, 1);
WITH DataSource AS
(
SELECT *
,COUNT(*) OVER (PARTITION BY [TransactionId], [Amount]) AS [Count]
FROM #DataSource
)
SELECT *
FROM DataSource
WHERE [Count] = 2
ORDER BY TransactionId ASC,Amount ASC, CrDr ASC;
Try this: This this simplest solution of this problem
;with cte
as
(
select TransactionId, Amount
from Table1
group by TransactionId, Amount
having count(*) > 1
)
select *
from Table1 t
inner join cte c on t.TransactionId = c.TransactionId and t.Amount = c.Amount

Five Columns to a single row

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

Generate three rows in select query

I have following table
SELECT TableCode, Col1, Col2
FROM TableA
WHERE TableCode = 23
Result of Table:
TableCode | Col1 | Col1
23 | CustCode | QS
23 | CatCode | QS
After that i wrote one query on TableA which return following output
Query :
SELECT TableCode,x.ColCode,
x.ColumnName + '_' + CONVERT(VARCHAR(5), ROW_NUMBER() OVER (PARTITION BY X.COL ORDER BY X.COL)) [ColumnName],X.Values,
ROW_NUMBER() OVER (PARTITION BY X.COL ORDER BY X.COL) [RowNo]
FROM TableA a CROSS APPLY
(SELECT 1 ColCode,'ParaName' ColumnName,Col1 Values
UNION ALL
SELECT 2,'ParaSource',Col2
) x
WHERE TableCode = 23;
Result :
TableCode | ColCode | ColumnName | Values | RowNo
23 | 1 | ParaName_1 | CustCode | 1
23 | 1 | ParaName_2 | CatCode | 2
23 | 2 | ParaSource_1 | QS | 1
23 | 2 | ParaSource_2 | QS | 2
And i required following output:
Required Output :
TableCode | ColCode | ColumnName | Values | RowNo
23 | 1 | ParaName_1 | CustCode | 1
23 | 1 | ParaName_2 | CatCode | 2
23 | 1 | ParaName_3 | Null | 3
23 | 2 | ParaSource_1 | QS | 1
23 | 2 | ParaSource_2 | QS | 2
23 | 2 | ParaSource_3 | null | 3
Using a couple of common table expressions and row_number() along with the table value constructor (values (...),(...))
to cross join numbers 1, 2, and 3 then using a left join to return 3 rows per TableCode even when you do not have three rows in the source table.
;with numbered as (
select *, rn = row_number() over (order by (select 1))
from TableA
where TableCode = 23
)
, cte as (
select distinct tc.TableCode, a.Col1, a.Col2, v.rn
from numbered tc
cross join (values (1),(2),(3)) v (rn)
left join numbered a
on a.TableCode = tc.TableCode
and a.rn = v.rn
)
select
a.TableCode
, x.ColCode
, [ColumnName] = x.ColumnName + '_' + convert(varchar(5),a.rn)
, X.Value
,[RowNo] = a.rn
from cte a
cross apply (values (1,'ParaName',Col1),(2,'ParaSource',Col2))
as x(ColCode, ColumnName, Value)
order by ColCode, RowNo;
rextester demo: http://rextester.com/CJU8986
returns:
+-----------+---------+--------------+----------+-------+
| TableCode | ColCode | ColumnName | Value | RowNo |
+-----------+---------+--------------+----------+-------+
| 23 | 1 | ParaName_1 | CustCode | 1 |
| 23 | 1 | ParaName_2 | CatCode | 2 |
| 23 | 1 | ParaName_3 | NULL | 3 |
| 23 | 2 | ParaSource_1 | QS | 1 |
| 23 | 2 | ParaSource_2 | QS | 2 |
| 23 | 2 | ParaSource_3 | NULL | 3 |
+-----------+---------+--------------+----------+-------+
This would appear to do what you want:
SELECT TableCode, x.ColCode, v.*
FROM TableA a CROSS APPLY
(VALUES (1, 'ParaName-1', Col1, 1),
(2, 'ParaName-2', Col2, 2),
(3, 'ParaName-3', NULL, 2)
) v(ColCode, ColumnName, [Values], RowNo)
WHERE TableCode = 23;
I see no reason to use row_number() when you can just read in the correct values. Also, VALUES is a SQL keyword so it is a really bad column name.

Group by following numbers with different values

ID | Week | BeginDate | EndDate | Value
1 | 38 | 14.9.2015 | 20.9.2015 | 100
2 | 39 | 21.9.2015 | 27.9.2015 | 100
3 | 40 | 28.9.2015 | 2.10.2015 | 100
4 | 42 | 12.10.2015 | 18.10.2015 | 100
5 | 43 | 19.10.2015 | 25.10.2015 | 100
6 | 44 | 26.10.2015 | 31.10.2015 | 80
How can I group this record for following weeks with same value.
The begindate and end date is also important.
In this case I expect 3 records:
StartDate | EndDate | Value
14.9.2015 | 02.10.2015 | 100
12.10.2015 | 25.10.2015 | 100
26.10.2015 | 31.10.2015 | 80
This could probably be made a lot more efficient than it currently is, but I believe it produces the desired output.
; WITH CTE1 AS (
SELECT T1.[Week]
, T1.[BeginDate]
, T1.[EndDate]
, T1.[Value]
, T2.[Week] [T2Week]
, ROW_NUMBER() OVER (ORDER BY T1.[Week]) [RN]
FROM tblName T1
LEFT JOIN (SELECT [Week], [Value] FROM tblName) T2 ON T1.[Week] = T2.[Week] + 1 AND T1.[Value] = T2.[Value])
, CTE2 AS (SELECT T3.[Week]
, T3.[BeginDate]
, T3.[EndDate]
, T3.[Value]
, T4.[EndDate] [T2EndDate]
, ROW_NUMBER() OVER (ORDER BY T3.[Week]) [RN]
FROM CTE1 T3
LEFT JOIN (SELECT [EndDate], [RN] FROM CTE1) T4 ON T3.[RN] = T4.[RN] + 1
WHERE T3.[T2Week] IS NULL)
SELECT T5.[BeginDate]
, ISNULL(T6.[T2EndDate], T5.[EndDate]) [T2EndDate]
, T5.[Value]
FROM CTE2 T5
LEFT JOIN (SELECT [T2EndDate], [RN] FROM CTE2) T6 ON T5.[RN] = T6.[RN] - 1
-- ORDER BY T5.[RN]

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) |