Extracting min&max in different categories - sql

I am trying to extract the minimum and maximum level in categories of customer's salary. I have the data set with 1K customers and I matched them to 13 categories regarding their salary: e.g. 'G: 130,000 - 149,999' or 'K: 250,000 - 299,999'. Minimum level for G: 130,000 - 149,999 (column 'min_level') is 130,000 and Maximum level (column 'max_level') is 149,999 for this group. I already figured it out for all numeric categories but I have an issue with ranges: A: below 30,000 (I want to extract the maximum 30K) & L: 300,000 and above (I want to extract the minimum 300K).
My code:
select distinct
cust_income_level,
substr(cust_income_level, 1, instr(cust_income_level, ' ')) as category,
substr(cust_income_level, 3, instr(cust_income_level, '-')-4) as min_level,
substr(cust_income_level, 13, instr(cust_income_level, '-')-3) as max_level
from
sh.customers
Result:
cust_income_level |category| min_level | max_level
G: 130,000 - 149,999| G: | 130,000 | 149,999
K: 250,000 - 299,999| K: | 250,000 | 299,999
...
A: Below 30,000| A: | (null) | (null)
L: 300,000 and above| L: | (null) | (null)

In my oppinion you should consider to perform case statement to particular columns:
select,
case
when
substr(cust_income_level,3,7) = '130,000' then 130.000
when
substr(cust_income_level,9,5) = 'Below' then 0
-- and rest for other min examples as next whens
else ''
end as
min_level
,
case
when
substr(cust_income_level,13,7) = '149,000' then 149.000
when
substr(cust_income_level,14,5) = 'above' then 500000000
-- and rest for other max examples as next whens
else ''
end as
max_level

If your data looks like here:
WITH
income_levels (CUST_INCOME_LEVEL) AS
(
SELECT 'A: Below 30,000' From DUAL UNION ALL
SELECT 'B: 30,000 - 49,999' From DUAL UNION ALL
SELECT 'C: 50,000 - 69,999' From DUAL UNION ALL
SELECT 'D: 70,000 - 89,999' From DUAL UNION ALL
SELECT 'E: 90,000 - 109,999' From DUAL UNION ALL
SELECT 'F: 110,000 - 129,999' From DUAL UNION ALL
SELECT 'G: 130,000 - 149,999' From DUAL UNION ALL
SELECT 'H: 150,000 - 179,999' From DUAL UNION ALL
SELECT 'I: 180,000 - 209,999' From DUAL UNION ALL
SELECT 'J: 210,000 - 249,999' From DUAL UNION ALL
SELECT 'K: 250,000 - 299,999' From DUAL UNION ALL
SELECT 'L: 300,000 and above' From DUAL
)
... then you should use case expression testing 'below' and 'above' first (CASE exits after first true condition) and then use SubStr in ELSE part.
Here is the code:
SELECT
SubStr(CUST_INCOME_LEVEL, 1, 1) "CATEGORY",
--
CASE
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'BELOW', 1, 1) > 0 THEN Null
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'ABOVE', 1, 1) > 0 THEN 300000
ELSE To_Number(REPLACE(SubStr(CUST_INCOME_LEVEL, InStr(CUST_INCOME_LEVEL, ': ', 1, 1) + 2, (InStr(CUST_INCOME_LEVEL, ' - ', 1, 1) - 1) - (InStr(CUST_INCOME_LEVEL, ': ', 1, 1) + 2) + 1 ), ',','') )
END "MIN_LEVEL",
--
CASE
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'BELOW', 1, 1) > 0 THEN 29999
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'ABOVE', 1, 1) > 0 THEN Null
ELSE To_Number(REPLACE(SubStr(CUST_INCOME_LEVEL, (InStr(CUST_INCOME_LEVEL, ' - ', 1, 1) + 4) - 1 ), ',', ''))
END "MAX_LEVEL"
FROM
income_levels
... which with above sample data results as:
CATEGORY MIN_LEVEL MAX_LEVEL
-------- ---------- ----------
A 29999
B 30000 49999
C 50000 69999
D 70000 89999
E 90000 109999
F 110000 129999
G 130000 149999
H 150000 179999
I 180000 209999
J 210000 249999
K 250000 299999
L 300000
Please do your own adjustments of the code if the format of your actual data is in any way different than the one I used here.
If you are not sure about lowest and highest level limits you can get them without hardcoding the numbers (29999 & 300000) if you change the code like below (the result is the same):
SELECT
SubStr(CUST_INCOME_LEVEL, 1, 1) "CATEGORY",
--
CASE
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'BELOW', 1, 1) > 0 THEN Null
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'ABOVE', 1, 1) > 0 THEN To_Number(REPLACE(SubStr(CUST_INCOME_LEVEL, 4, InStr(UPPER(CUST_INCOME_LEVEL), ' AND', 1, 1) -4), ',', ''))
ELSE To_Number(REPLACE(SubStr(CUST_INCOME_LEVEL, InStr(CUST_INCOME_LEVEL, ': ', 1, 1) + 2, (InStr(CUST_INCOME_LEVEL, ' - ', 1, 1) - 1) - (InStr(CUST_INCOME_LEVEL, ': ', 1, 1) + 2) + 1 ), ',','') )
END "MIN_LEVEL",
--
CASE
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'BELOW', 1, 1) > 0 THEN To_Number(REPLACE(SubStr(CUST_INCOME_LEVEL, InStr(Upper(CUST_INCOME_LEVEL), 'BELOW', 1, 1) + 6), ',', '')) - 1
WHEN InStr(Upper(CUST_INCOME_LEVEL), 'ABOVE', 1, 1) > 0 THEN Null
ELSE To_Number(REPLACE(SubStr(CUST_INCOME_LEVEL, (InStr(CUST_INCOME_LEVEL, ' - ', 1, 1) + 4) - 1 ), ',', ''))
END "MAX_LEVEL"
FROM
income_levels

Related

Oracle SQL display distinct none "standard alphanumeric caracters

I need to find the way to list all the characters used in the column in order to narrow down the "Approved" values within the insert template we are creating...
the idea is to allow all letters (only standard) without any dialect / country specific ones.
trying something like this... but need to have a list of the characters left over... like "$%()* etc.
SELECT * FROM mytable WHERE REGEXP_LIKE(column_1,^[a-zA-Z0-9-]+$)
To find the other characters, you could remove the ones you do expect and then see what is left:
SELECT REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' ) AS other_characters
FROM mytable
WHERE REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' ) IS NOT NULL
If you want to concatenate and remove duplicate characters:
WITH replace_expected ( str ) AS (
SELECT REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' )
FROM mytable
WHERE REGEXP_REPLACE( column1, '[a-zA-Z0-9-]' ) IS NOT NULL
),
split_strings ( str, pos, ch ) AS (
SELECT str, 1, SUBSTR(str, 1, 1)
FROM replace_expected
UNION ALL
SELECT str, pos + 1, SUBSTR(str, pos + 1, 1)
FROM split_strings
WHERE pos < LENGTH(str)
)
SELECT LISTAGG(DISTINCT ch) WITHIN GROUP (ORDER BY ch) AS other_characters
FROM split_strings;
fiddle
Two steps:
Extract the special characters from all strings in the column and concatenate them into one long string (with possibly many characters appearing multifold in the string).
Use a recursive query to loop through the characters and return a string with distinct characters.
The query:
with one_row (str) as
(
select listagg(regexp_replace(column_1, '[a-zA-Z0-9\-]'))
from mytable
where regexp_like(column_1, '[^a-zA-Z0-9\-]')
)
select listagg(distinct substr(str, level, 1)) as c
from one_row
connect by level <= length(str);
create table dummy_data as
select rownum as id, dbms_random.string(opt, len) str
from (
select case round(dbms_random.value(0, 3))
when 0 then 'a'
when 1 then 'x'
else 'p'
end opt,
round(dbms_random.value(5, 60)) len
from dual connect by level <= 30 );
SELECT * FROM DUMMY_DATA;
ID STR
1 UMUUJ0R5VM1T3X10TDCNIWC3MQ5ELOB041YMNEJSLT
2 _t8 }LeZhjiMB"8/a'/~a
3 BLSE6XX6SL3M7W0DG3HH28SCHPSAT11ZH2E5DOSKEV3KW9
4 1]Mh58(l<Wa}{
5 :_QiWUkwp}V$}O
6 NC911A4SRN35CNXT2EU5H2GZ67IQQLKH
7 e"8,z$=Yvy5egvEH2KUkNoVjkitd9IMm0ZktsB i(bk4uU]c3;E
8 MgbpIsLZpWEcAghOUKOISA
9 7H02ASKO3CZRN4D5FUNPEU6YUZD
10 KbJ+QrI\l.th%>^f!Io%wshsVA%
11 PO9A47VU7AXI17XYD5VMSWW8E
12 1ILWL4V
13 FgubwibYBytNvmJHxUfG
14 ?[ngH?0!k.onN>mF(nrkO
15 86G0HP3
16 WXDBV3OBMVSDKQ59YT73G0II3U94
17 GP375CFIQPPN6216I5A7L54O
18 i\L<K,"d'ye 6s~_MB0O1 aC$q;T"EaqpZ^s\gIiYu&:%OnhVj]<a]CmOgqM
19 WxUEtr\II(97i7PQ-Z]yqd#&`#CQB0M"c0;{.by9qo#HT
20 IF5OP7KS9AXW91
21 HNcKwxXozXjTVwKeFZDLdmNOzFKKKq
22 4D8CINXIVT244RDDRZ5TSDQ4CRF4
23 3)oxevW-(~=+#cP[^g)##|1.TL-_N9O-Zdgj"cwJC'*NR; FtK)K
24 AndzeLIEPklDuTWWEBrKrdNKdXwMGeLauJkRzKpKHEGAsxlEXliwBTHdK
25 dlEX1tGFuU5\5+{5`R
26 /W0.{B&)ax&lWEE#OSw
27 CBKOVLKDFKC3EVR
28 :V#Lc.Z"8[O-)cAWUpMjc?j\Kj?xV#%`Yp [VkEV1
29 P9P047
30 W)S<fB`F;N_brMP
with
h (ok_chars) as (
select '0123456789' || 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ||
'abcdefghijklmnopqrstuvwxyz'
from dual
)
select c, count(*) as cnt
from dummy_data cross join lateral (
select substr(str, level, 1) as c
from h
where instr(ok_chars, substr(str, level, 1)) = 0
connect by level <= length(str)
)
group by c;
C CNT
} 4
- 5
' 3
< 4
, 2
= 2
_ 5
( 5
^ 3
[ 4
" 7
/ 3
: 3
. 6
% 5
! 2
? 4
` 4
8
; 5
+ 3
> 2
) 6
| 1
* 1
~ 3
\ 6
& 4
# 5
] 5
{ 4
# 4
$ 3

Count based on a date condition in PLSQLDev

I need help to do a count based on a date condition.
I have a DB similar to the following:
ManDB
ID
report_date
traffic_v
traffic_ul
traffic_dl
a
1/12/2021
0
0
100
a
2/12/2021
0
0
100
a
3/12/2021
100
0
100
a
4/12/2021
100
0
100
b
1/12/2021
0
100
100
b
2/12/2021
0
0
0
b
3/12/2021
0
100
0
b
4/12/2021
100
100
0
I need you to count the data to zero, for which I have the query:
SELECT
ID AS SECTOR,
SUM(TRAFFIC) TRAFICO_VOZ,
SUM(TRAFFIC_DL_G) + SUM(TRAFFIC_DL_E) TRAFFIC_DL,
SUM(TRAFFIC_UL_G) + SUM(TRAFFIC_UL_E) TRAFFIC_UL
FROM
MainDB
GROUP BY ID
HAVING SUM(TRAFFIC) = 0
OR (SUM(TRAFFIC_DL_G) + SUM(TRAFFIC_DL_E)) = 0
OR (SUM(TRAFFIC_UL_G) + SUM(TRAFFIC_UL_E)) = 0
But I need you to count me from the current date backwards, how many days has it been zero
You should only count me from the last record in zero.
So you should get the following result:
Expected result
ID
traffic_v
count_v
traffic_ul
count_ul
traffic_dl
count_dl
a
200
0
0
4
400
0
b
100
0
200
0
0
3
I do not know how to set the condition so that it detects the date on which I began to have zero records and perform the count of days until the current date.
In cases where the register is different from zero, the count must be restarted.
The db is updated daily.
the counts are displayed correctly with the query, as I only care about zero data.
try to use SUM / CASE, but it counts me from the minimum date that it finds at zero, regardless of having a different record
You can use a MODEL clause:
SELECT id,
count_traffic_v,
sum_traffic_v,
count_traffic_ul,
sum_traffic_ul,
count_traffic_dl,
sum_traffic_dl
FROM (
SELECT *
FROM (
SELECT m.*,
ROW_NUMBER() OVER (PARTITION BY id ORDER BY report_date DESC) AS rn
FROM mainDB m
)
MODEL
PARTITION BY (id)
DIMENSION BY (report_date)
MEASURES (
rn,
traffic_v,
0 AS count_traffic_v,
0 AS sum_traffic_v,
traffic_ul,
0 AS count_traffic_ul,
0 AS sum_traffic_ul,
traffic_dl,
0 AS count_traffic_dl,
0 AS sum_traffic_dl
)
RULES AUTOMATIC ORDER (
count_traffic_v[report_date] = CASE traffic_v[cv()]
WHEN 0
THEN COALESCE(count_traffic_v[cv() - 1] + 1, 1)
ELSE 0
END,
sum_traffic_v[report_date] = CASE traffic_v[cv()]
WHEN 0
THEN 0
ELSE COALESCE(sum_traffic_v[cv() - 1], 0) + traffic_v[cv()]
END,
count_traffic_ul[report_date] = CASE traffic_ul[cv()]
WHEN 0
THEN COALESCE(count_traffic_ul[cv() - 1] + 1, 1)
ELSE 0
END,
sum_traffic_ul[report_date] = CASE traffic_ul[cv()]
WHEN 0
THEN 0
ELSE COALESCE(sum_traffic_ul[cv() - 1], 0) + traffic_ul[cv()]
END,
count_traffic_dl[report_date] = CASE traffic_dl[cv()]
WHEN 0
THEN COALESCE(count_traffic_dl[cv() - 1] + 1, 1)
ELSE 0
END,
sum_traffic_dl[report_date] = CASE traffic_dl[cv()]
WHEN 0
THEN 0
ELSE COALESCE(sum_traffic_dl[cv() - 1], 0) + traffic_dl[cv()]
END
)
)
WHERE rn = 1;
Which, for the sample data:
CREATE TABLE maindb (ID, report_date, traffic_v, traffic_ul, traffic_dl) AS
SELECT 'a', DATE '2021-12-01', 0, 0, 100 FROM DUAL UNION ALL
SELECT 'a', DATE '2021-12-02', 0, 0, 100 FROM DUAL UNION ALL
SELECT 'a', DATE '2021-12-03', 100, 0, 100 FROM DUAL UNION ALL
SELECT 'a', DATE '2021-12-04', 100, 0, 100 FROM DUAL UNION ALL
SELECT 'b', DATE '2021-12-01', 0, 100, 100 FROM DUAL UNION ALL
SELECT 'b', DATE '2021-12-02', 0, 0, 0 FROM DUAL UNION ALL
SELECT 'b', DATE '2021-12-03', 0, 100, 0 FROM DUAL UNION ALL
SELECT 'b', DATE '2021-12-04', 100, 100, 0 FROM DUAL;
Outputs:
ID
COUNT_TRAFFIC_V
SUM_TRAFFIC_V
COUNT_TRAFFIC_UL
SUM_TRAFFIC_UL
COUNT_TRAFFIC_DL
SUM_TRAFFIC_DL
a
0
200
4
0
0
400
b
0
100
0
200
3
0
db<>fiddle here

sql grouping grades

I have a table for subjects as follows:
id Subject Grade Ext
100 Math 6 +
100 Science 4 -
100 Hist 3
100 Geo 2 +
100 CompSi 1
I am expecting output per student in a class(id = 100) as follows:
Grade Ext StudentGrade
6 + 1
6 0
6 - 0
5 + 0
5 0
5 - 0
4 + 0
4 0
4 - 1
3 + 0
3 1
3 - 0
2 + 1
2 0
2 - 0
1 + 0
1 1
1 - 0
I would want this done on oracle/sql rather than UI. Any inputs please.
You should generate rows first, before join them with your table like below. I use the with clause here to generate the 18 rows in your sample.
with rws (grade, ext) as (
select ceil(level/3), decode(mod(level, 3), 0, '+', 1, '-', null)
from dual
connect by level <= 3 * 6
)
select r.grade, r.ext, nvl2(t.Ext, 1, 0) studentGrade
from rws r
left join your_table t
on t.Grade = r.Grade and decode(t.Ext, r.Ext, 1, 0) = 1
order by 1 desc, decode(r.ext, null, 2, '-', 3, '+', 1)
You could do something like this. In the WITH clause I generate two small "helper" tables (really, inline views) for grades from 1 to 6 and for "extensions" of +, null and -. In the "extensions" view I also create an "ordering" column to use in ordering the final output (if you are wondering why I included that).
Also in the WITH clause I included sample data - you will have to remove that and instead use your actual table name in the main query.
The idea is to cross-join "grades" and "extensions", and left-outer-join the result to your input data. Count the grades from the input data, grouped by grade and extension, and after filtering the desired id. The decode thing in the join condition is needed because for extension we want to treat null as equal to null - something that decode does nicely.
with
sample_inputs (id, subject, grade, ext) as (
select 100, 'Math' , 6, '+' from dual union all
select 100, 'Science', 4, '-' from dual union all
select 100, 'Hist' , 3, null from dual union all
select 100, 'Geo' , 2, '+' from dual union all
select 100, 'CompSi' , 1, null from dual
)
, g (grade) as (select level from dual connect by level <= 6)
, e (ord, ext) as (
select 1, '+' from dual union all
select 2, null from dual union all
select 3, '-' from dual
)
select g.grade, e.ext, count(t.grade) as studentgrade
from g cross join e left outer join sample_inputs t
on t.grade = g.grade and decode(t.ext, e.ext, 0) = 0
and t.id = 100 -- change this as needed!
group by g.grade, e.ext, e.ord
order by g.grade desc, e.ord
;
OUTPUT:
GRADE EXT STUDENTGRADE
----- --- ------------
6 + 1
6 0
6 - 0
5 + 0
5 0
5 - 0
4 + 0
4 0
4 - 1
3 + 0
3 1
3 - 0
2 + 1
2 0
2 - 0
1 + 0
1 1
1 - 0
It looks like you want sparse data to be filled in as part of joining students and subjects.
Since Oracle 10g the correct way to do this has been with a "partition outer join".
The documentation has examples.
https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/SELECT.html#GUID-CFA006CA-6FF1-4972-821E-6996142A51C6

SQL Query - Help Needed for retrieval of values in a text field

I have a table that looks like follows:
NAME(varchar(6), STRING(varchar(250)
ABCD '1 2 1 173 1 8 9 1 1 2 4 7 1 3.....'
APLC '1 3 11 34 1 4 99 33 23 111 12 6 7 8....'
the string continues with this up to 250 characters.
What I am trying to do is get the values and there respective positions from this string.
I know I can use Charindex but that gives me only the first position of a number in the string.
e.g.
Select Charindex ('2',STRING) where Name = ABCD
ANSWER = 7
But what I'm looking for is something like a table that have the following in for each Name
Name Position Value
---------------------------
ABCD, 7, 2
ABCD, 1, 1
ABCD, 13, 1
ABCD, 18, 1
ABCD, 19, 7
Any ideas welcome :)
With a little help of a numbers table it could look like this.
select T.Name,
N.N as Position,
substring(T.STRING, N.N, 1) as Value
from YourTable as T
cross apply Numbers as N
where N.N between 1 and 250 and
substring(T.STRING, N.N, 1) <> ' '
Working sample with table variable and master..spt_values as a numbers table.
declare #T table
(
NAME varchar(6),
STRING varchar(250)
)
insert into #T values
('ABCD', '1 2 1 173 1 8 9 1 1 2 4 7 1 3'),
('APLC', '1 3 11 34 1 4 99 33 23 111 12 6 7 8')
;with Numbers(N) as
(
select Number
from master..spt_values
where type = 'P'
)
select T.Name,
N.N as Position,
substring(T.STRING, N.N, 1) as Value
from #T as T
cross apply Numbers as N
where N.N between 1 and 250 and
substring(T.STRING, N.N, 1) <> ' '
This approach would work for multi-digit numbers. If 173 should result in three result rows, check Mikael Eriksson's or podiluska's answer.
; with cte as
(
select 1 as start
, case
when patindex('%[0-9] %', string) > 0 then patindex('%[0-9] %', string)
else len(string)
end as [length]
, name
, string
from YourTable
union all
select start + [length] as start
, case
when patindex('%[0-9] %',
substring(string, start + [length], len(string)-start + [length]))
> 0 then patindex('%[0-9] %',
substring(string, start + [length], len(string)-start + [length]))
else len(string)-start + [length]
end as [length]
, name
, string
from cte
where start + [length] < len(string)
)
select Name
, start + patindex('%[0-9]%', substring(string, [start], [length])) - 1 as Position
, ltrim(substring(string, [start], [length])) as Value
from cte
Live example at SQL Fiddle.
Where #t is your table...
;WITH numbers ( n ) AS
(
select 1 as n
union all
select 1 + n FROM numbers WHERE n < 250
)
select name, n as position, SUBSTRING(string,n,1) as value
from #t, numbers
where SUBSTRING(string,n,1)<>' '
order by Name, n
option (maxrecursion 250)
If, on the other hand, you want to treat consecutive numbers as being a one number...
;WITH Pieces(name, pn, start, [stop], string) AS
(
SELECT name, 1, 1, CHARINDEX(' ', string),string from #t
UNION ALL
SELECT pieces.name, pn + 1, stop + 1, CHARINDEX(' ', pieces.string, stop + 1), pieces.string
FROM Pieces
inner join #t on pieces.name = #t.name
WHERE stop > 0
)
select *
from
( SELECT name, start as position,
SUBSTRING(string, start, CASE WHEN stop > 0 THEN stop-start ELSE 300 END) AS value
FROM Pieces
) v
where RTRIM(value)>''

Unpivot or Something else

I have a table with 2 columns that should only contain one value but some entries contains 2 or 3 values . all the other columns are the same for these problem rows .
Table A - Currently
Deal ID | PA ID | other columns
1 2 xxxxx
1,2 2 xxxxx
3 1,5 xxxxx
What I want
Deal ID | PA ID | other columns
1 2 xxxxx
1 2 xxxxx
2 2 xxxxx
3 1 xxxxx
3 5 xxxxx
Not sure how to do this ? Think I need to UNPIVOT and then remove the ,.
Here is one solution. It is brute force and uses union all to bring get multiple copies:
with incols as (
select (case when charindex(Dealid, ',') > 0
then left(DealId, charindex(Dealid, ',') - 1)
else DealId
end) as DealId1,
(case when charindex(Dealid, ',') > 0
then substring(DealId, charindex(DealId, ',') + 1, 100)
end) as DealId2,
(case when charindex(PAId, ',') > 0
then left(PAId, charindex(PAId, ',') - 1)
else PAId
end) as PAId1,
(case when charindex(PAId, ',') > 0
then substring(PAId, charindex(PAId, ',') + 1, 100)
end) as PAId2,
t.*
from t
),
deals as (
select (case when whichdeal = 1 then deal1 else deal2 end) as newdeal, t.*
from ((select *, 1 as whichdeal
from t
) union all
(select *, 2 as whichdeal
from t
where deal2 is not null
)) t
)
select newdeal as dealid, t.*
from deals
Including PAs requires adding another CTE, and then joining deals and PAs on dealid and PA id to get all possible combinations. You didn't specify exactly what you want to happen when there are duplicates in both rows, so I'm just guessing that you would want all combinations.
The solution was :
DECLARE #t TABLE (
DealID VARCHAR(10),
PAID VARCHAR(200),
[DESC] VARCHAR(100))
INSERT #t
SELECT '1',
'2',
'xxxx'
UNION ALL
SELECT '1,2',
'2',
'xxxx'
UNION ALL
SELECT '3',
'1,5',
'xxxx'
SELECT LEFT(b, Charindex(',', b + ',') - 1) AS DealID,
LEFT(d, Charindex(',', d + ',') - 1) AS PAID,
[Desc]
FROM (SELECT Substring(DealID, DE.number, 200) AS b,
Substring(PAID, PA.number, 200) AS d,
[Desc]
FROM #t DealID
LEFT JOIN (SELECT DISTINCT number
FROM master.dbo.spt_values
WHERE number BETWEEN 1 AND 200) PA
ON Substring(',' + PAID, PA.number, 1) = ','
LEFT JOIN (SELECT DISTINCT number
FROM master.dbo.spt_values S
WHERE number BETWEEN 1 AND 200) DE
ON Substring(',' + DealID, DE.number, 1) = ',') t