create one list from to two columns - sql

in need Help with oracle SQL.
I have a table with
from to
F B
B R
R D
E X
X Q
and I need the list
F
B
R
D
E
X
Q
so my problem is the jump from R-->D to E-->X
Edit: It's a big list with from and to, seperatet with a annother column as citerium. Normaly there is every from in the to column, so i used
SELECT from,snr as Nr FROM list where StrAbsNr = 1
union all
SELECT to,snr + 1 as Nr FROM list
to create a ordered list. But there are gaps in some parts, in the example there is D-->E missing
has anybody an idea ?

for your example this work:
WITH ft AS
(SELECT 'f' vfrom, 'b' AS vto FROM dual UNION ALL
SELECT 'b' , 'r' FROM dual UNION ALL
SELECT 'r','d' FROM dual UNION ALL
SELECT 'e','x' FROM dual UNION ALL
SELECT 'x','q' FROM dual )
SELECT a.a, MAX(rn), MIN(ob)
FROM
( SELECT vfrom a , rownum rn, 1 ob FROM ft
UNION ALL
SELECT vto , rownum rn, 2 ob FROM ft
) a
GROUP BY a
ORDER BY MAX(rn), MIN(ob)
A MAX(RN) MIN(OB)
- ---------- ----------
f 1 1
b 2 1
r 3 1
d 3 2
e 4 1
x 5 1
q 5 2
7 rows selected
or analityc func row_number:
SELECT *
FROM
(SELECT a.a,
row_number() over (partition BY a order by rn, ob) rna,
ob,
rn
FROM
( SELECT vfrom a, rownum rn, 1 ob FROM ft
UNION ALL
SELECT vto , rownum rn, 2 ob FROM ft
) a
)
WHERE rna=1
ORDER BY rn,
ob
A RNA OB RN
- ---------- ---------- ----------
f 1 1 1
b 1 2 1
r 1 2 2
d 1 2 3
e 1 1 4
x 1 2 4
q 1 2 5
7 rows selected

select "from" as val from table
union
select to from table
And if you want to keep the order:
select distinct val
from (select "from" as val, rownum, 1 as valOrder from table
union
select to, rownum, 2 as valOrder from table)
order by rownum,valOrder

Related

SQL Grouping by first digit from sets of record

I need your help in SQL
I have a set of records of Cost center ID below.
what I want to do is to segregate/group them by inserting column to distinguish the category.
as you can see all digits start in 7 is belong to the bold digits.
my expected out is on below image also.
You can as the below:
DECLARE #Tbl TABLE (ID INT)
INSERT INTO #Tbl
VALUES
(735121201),
(735120001),
(5442244),
(735141094),
(735141097),
(4008060),
(735117603),
(40100000),
(735142902),
(735151199),
(4010070)
;WITH TableWithRowId
AS
(
SELECT
ROW_NUMBER() OVER (ORDER BY(SELECT NULL)) RowId,
ID
FROM
#Tbl
), TempTable
AS
(
SELECT T.RowId + 1 AS RowId FROM TableWithRowId T
WHERE
LEFT(T.ID, 1) != 7
), ResultTable
AS
(
SELECT
T.RowId ,
T.ID,
DENSE_RANK() OVER (ORDER BY (SELECT TOP 1 A.RowId FROM TempTable A WHERE A.RowId > T.RowId ORDER BY A.RowId)) AS Flag
FROM TableWithRowId T
)
SELECT * FROM ResultTable
Result:
RowId ID Flag
----------- ----------- ----------
1 735121201 1
2 735120001 1
3 5442244 1
4 735141094 2
5 735141097 2
6 4008060 2
7 735117603 3
8 40100000 3
9 735142902 4
10 735151199 4
11 4010070 4
The following query is similer with NEER's
;WITH test_table(CenterID)AS(
SELECT '735121201' UNION ALL
SELECT '735120001' UNION ALL
SELECT '5442244' UNION ALL
SELECT '735141094' UNION ALL
SELECT '735141097' UNION ALL
SELECT '4008060' UNION ALL
SELECT '735117603' UNION ALL
SELECT '40100000' UNION ALL
SELECT '735142902' UNION ALL
SELECT '735151199' UNION ALL
SELECT '4010070'
),t1 AS (
SELECT *,ROW_NUMBER()OVER(ORDER BY(SELECT 1)) AS rn,CASE WHEN LEFT(t.CenterID,1)='7' THEN 1 ELSE 0 END AS isSeven
FROM test_table AS t
),t2 AS(
SELECT t1.*,ROW_NUMBER()OVER(ORDER BY t1.rn) AS toFilter
FROM t1 LEFT JOIN t1 AS pt ON pt.rn=t1.rn-1
WHERE pt.CenterID IS NULL OR (t1.isSeven=1 AND pt.isSeven=0)
)
SELECT t1.CenterID,x.toFilter FROM t1
CROSS APPLY(SELECT TOP 1 t2.toFilter FROM t2 WHERE t2.rn<=t1.rn ORDER BY rn desc) x
CenterID toFilter
--------- --------------------
735121201 1
735120001 1
5442244 1
735141094 2
735141097 2
4008060 2
735117603 3
40100000 3
735142902 4
735151199 4
4010070 4

How to combine and count two columns in oracle?

My table seems like this;
A B
1 100
1 102
1 105
2 100
2 105
3 100
3 102
I want output like this:
A Count(B)
1 3
1,2 2
1,2,3 3
2 2
3 2
2,3 2
How can i do this?
I try to use listagg but it didn't work.
I suspect that you want to count the number of sets of A that are in the data -- and that your sample results are messed up.
If so:
select grp, count(*)
from (select listagg(a, ',') within group (order by a) as grp
from t
group by b
) b;
This gives you the counts for the full combinations present in the data. The results would be:
1,2,3 1
1,3 1
1,2 1
You can get the original number of rows by doing:
select grp, sum(cnt)
from (select listagg(a, ',') within group (order by a) as grp, count(*) as cnt
from t
group by b
) b;
Oracle Setup:
CREATE TABLE table_name ( A, B ) AS
SELECT 1, 100 FROM DUAL UNION ALL
SELECT 1, 102 FROM DUAL UNION ALL
SELECT 1, 105 FROM DUAL UNION ALL
SELECT 2, 100 FROM DUAL UNION ALL
SELECT 2, 105 FROM DUAL UNION ALL
SELECT 3, 100 FROM DUAL UNION ALL
SELECT 3, 102 FROM DUAL;
Query:
SELECT A,
COUNT(B)
FROM (
SELECT SUBSTR( SYS_CONNECT_BY_PATH( A, ',' ), 2 ) AS A,
B
FROM table_name
CONNECT BY PRIOR B = B
AND PRIOR A + 1 = A
)
GROUP BY A
ORDER BY A;
Output:
A COUNT(B)
----- ----------
1 3
1,2 2
1,2,3 1
2 2
2,3 1
3 2

In Oracle, how to do Rank() (or any other ways that work) down to group level instead of row level?

Let's say we have this set of code:
NAM T F
A 10 Y
A 11 N
A 12 N
A 13 Y
B 10 Y
B 11 Y
How can we use Rank() (or any other ways that work) to transform the above to:
NAM F ID MNT CNT
A Y 1 10 1
A N 2 11 2
A Y 3 13 1
B Y 1 11 2
(NAM and F is grouped, but for F they can be grouped only when the rows are next to each other - in other words, for F to be grouped togather, the rows must have the value of T = 0,1,2,3,4... the difference of each T must be exactly 1)
The new columns are ID and CNT - the main point is column ID, where the 2nd and 3rd row have to be considered to be in the same rank (ID=2) because both rows have the F flag as false/0.
The source data can be got from:
select 'A' NAM, 10 t, 'Y' f FROM dual union all
select 'A' NAM, 11 t, 'N' f FROM dual union all
select 'A' NAM, 12 t, 'N' f FROM dual union all
select 'A' NAM, 13 t, 'Y' f FROM dual union all
select 'B' NAM, 10 t, 'Y' f FROM dual union all
select 'B' NAM, 11 t, 'Y' f FROM dual
The order of the time field T has to be considered, in other words the following result should not result:
NAM F ID MNT CNT
A Y 1 10 2
A N 2 11 2
B Y 1 10 2
One more example:
NAM T F
A 10 Y
A 11 N
A 12 Y
A 13 Y
A 14 N
A 15 N
A 16 N
A 17 Y
B 10 Y
B 11 Y
Result should be:
NAM F ID MNT CNT
A Y 1 10 1
A N 2 11 1
A Y 3 12 2
A N 4 14 3
A Y 5 17 1
B Y 1 10 2
The source data set:
select 'A' NAM, 0 t, 'Y' f FROM dual union all
select 'A' NAM, 1 t, 'N' f FROM dual union all
select 'A' NAM, 2 t, 'Y' f FROM dual union all
select 'A' NAM, 3 t, 'Y' f FROM dual union all
select 'A' NAM, 4 t, 'N' f FROM dual union all
select 'A' NAM, 5 t, 'N' f FROM dual union all
select 'A' NAM, 6 t, 'N' f FROM dual union all
select 'A' NAM, 7 t, 'Y' f FROM dual union all
select 'B' NAM, 0 t, 'Y' f FROM dual union all
select 'B' NAM, 1 t, 'Y' f FROM dual
If you need to count consecutive rows in partitions by column A you could use this technique:
select a, min(f) f, rank() over (partition by a order by diff) i, count(1) cnt
from (
select test.*,
row_number() over (partition by a order by t)
- count(f) over (partition by a, f order by t) diff
from test)
group by a, diff order by a, diff
SQLFiddle
Edit: for updated part of question use these modifications
select nam, mnt, cnt,
row_number() over (partition by nam, diff order by mnt) id
from (
select nam, min(t) mnt, count(1) cnt, diff
from (
select nam, t, f,
row_number() over (partition by nam order by t)
- count(1) over (partition by nam, f order by t) diff
from test )
group by nam, diff, f )
order by nam, diff
This query gave me expected result, please test it.
Can be solved with "stacked" analytics:
Identify the "leader" of each group to be aggregated.
Propagate the "leader" to the rest of the elements in the group.
Aggregate the data via the "leader".
The query would be:
with source_data$ as (
/*
... your source data here ...
*/
),
find_the_leader$ as (
select X.*,
case when lag(t) over (partition by nam, f order by t asc) is null
or t != 1 + lag(t) over (partition by nam, f order by t asc)
then t
end as mnt_leader
from source_data$ X
),
propagage_the_leader$ as (
select X.*,
last_value(mnt_leader) ignore nulls over (partition by nam, f order by t asc) as mnt
from find_the_leader$ X
)
select nam, f,
row_number() over (partition by nam order by mnt asc) as id,
mnt, count(1) as cnt
from propagage_the_leader$
group by nam, f, mnt
order by nam, id, f
;
On my PC, with your source data no.1 it yields:
NAM F ID MNT CNT
A Y 1 10 1
A N 2 11 2
A Y 3 13 1
B Y 1 10 2
And with your source data no.2 (with values of t increased by 10 in the "union-all-from-dual" select) it yields:
NAM F ID MNT CNT
A Y 1 10 1
A N 2 11 1
A Y 3 12 2
A N 4 14 3
A Y 5 17 1
B Y 1 10 2
I hope that you don't have any additional constraints over how your results should look like, because I don't have more time to adjust the query to answer a different problem.
Using the well known two step approach to identify contignous groups (LAG / LAST_VALUE ignore nulls) here the query (updated for the new setup)
with tab1 as (
select nam,t,f,
nvl(lag(f) over (partition by nam order by t),-1) lag_f,
case when (nvl(lag(f) over (partition by nam order by t),-1) <> f) then
row_number() over (partition by nam order by t) end grp
from tst
), tab2 as (
select nam,t,f,
last_value(grp ignore nulls) over (partition by nam order by t) as grp2
from tab1
), tab3 as (
select
nam, f, count(*) cnt, min(t) mnt
from tab2
group by nam, f, grp2
)
select nam,f,
rank() over (partition by nam order by mnt) r,
mnt, cnt
from tab3
order by nam,4;
gives
NAM, F, R, MNT, CNT
A Y 1 0 1
A N 2 1 1
A Y 3 2 2
A N 4 4 3
A Y 5 7 1
B Y 1 0 2

Oracle SQL -- Combining two tables, but taking duplicates from one?

I have these tables:
Table A
Num Letter
1 A
2 B
3 C
Table B
Num Letter
2 C
3 D
4 E
I want to union these two tables, but I only want each number to appear once. If the same number appears in both tables, I want it from Table B instead of table A.
Result
Num Letter
1 A
2 C
3 D
4 E
How could I accomplish this? A union will keep duplicates and an intersect would only catch the same rows -- I consider a row a duplicate when it has the same number, regardless of the letter.
Try this: http://www.sqlfiddle.com/#!4/0b796/1
with a as
(
select Num, 'A' as src, Letter
from tblA
union
select Num, 'B' as src, Letter
from tblB
)
select
Num
,case when count(*) > 1 then
min(case when src = 'B' then Letter end)
else
min(Letter)
end as Letter
from a
group by Num
order by Num;
Output:
| NUM | LETTER |
----------------
| 1 | A |
| 2 | C |
| 3 | D |
| 4 | E |
And another one:
SELECT COALESCE(b.num, a.num) num, COALESCE(b.letter, a.letter) letter
FROM a FULL JOIN b ON a.num = b.num
ORDER BY 1;
With your data:
WITH a AS
(SELECT 1 num, 'A' letter FROM dual
UNION ALL SELECT 2, 'B' FROM dual
UNION ALL SELECT 3, 'C' FROM dual),
b AS
(SELECT 2 num, 'C' letter FROM dual
UNION ALL SELECT 3, 'D' FROM dual
UNION ALL SELECT 4, 'E' FROM dual)
SELECT COALESCE(b.num, a.num) num, COALESCE(b.letter, a.letter) letter
FROM a FULL JOIN b ON a.num = b.num
ORDER BY 1;
NUM L
---------- -
1 A
2 C
3 D
4 E
The efficiency might be lacking, but it produces the correct answer.
select nums.num, coalesce(b.letter, a.letter)
from
(select num from b
union
select num from a) nums
left outer join b
on (b.num = nums.num)
left outer join a
on (a.num = nums.num);
Or you can use Oracle-specific technique to make the code shorter: http://www.sqlfiddle.com/#!4/0b796/11
with a as
(
select Num, 'A' as src, Letter
from tblA
union
select Num, 'B' as src, Letter
from tblB
)
select Num, min(Letter) keep(dense_rank first order by src desc) as Letter
from a
group by Num
order by Num;
Output:
| NUM | LETTER |
----------------
| 1 | A |
| 2 | C |
| 3 | D |
| 4 | E |
The code works regardless of min(letter) or max(letter), it has the same output, it gives the same output. Important is you use keep dense_rank. Another important thing is, the order matter, we use order by src desc to give priority to source table B when keeping a row.
And to really make it shorter, use keep dense_rank last, and omit the desc on order by, asc is the default anyway http://www.sqlfiddle.com/#!4/0b796/12
with a as
(
select Num, 'A' as src, Letter
from tblA
union
select Num, 'B' as src, Letter
from tblB
)
select Num, min(Letter) keep(dense_rank last order by src) as Letter
from a
group by Num
order by Num;
Again, using min or max on Letter doesn't matter, as long as your keep dense_rank get the prioritized/preferred row
Another option is to combine the UNION and MINUS commands as follows:
SELECT
NUM, LETTER
FROM
TABLE B
UNION
( SELECT
NUM, LETTER
FROM
TABLE A
WHERE
NUM IN (SELECT
NUM
FROM
TABLE A
MINUS
SELECT
NUM
FROM
TABLE B ))
SELECT A.*
FROM A
WHERE A.NUM NOT IN
(SELECT A.NUM
FROM B
WHERE A.NUM=B.NUM
AND B.NUM IS NOT NULL
AND A.NUM IS NOT NULL
)
UNION
SELECT * FROM B;

sql server : count records

I have a tableA (ID int, Match varchar, tot int)
ID Match Tot
1 123
2 123
3 12
4 12
5 4
6 12
7 8
Now, I want to calculate Tot which is total number of match exists in the table. for example 123 occured twice, 12 exist thrice and so on. Also note that I want the count only at first match. here is the expected result.:
ID Match Tot
1 123 2
2 123
3 12 3
4 12
5 4 1
6 12
7 8 1
Another case:
ID Match Count Tot
1 123 2
2 123 1
3 12 10
4 12 10
5 4 3
6 12 5
7 8 7
Now I want to add the count for the same match. expected result:
ID Match Count Tot
1 123 2 3
2 123 1
3 12 10 25
4 12 10
5 4 3 3
6 12 5
7 8 7 7
Thanks
WITH tableA(ID, Match) AS
(
SELECT 1,123 UNION ALL
SELECT 2,123 UNION ALL
SELECT 3,12 UNION ALL
SELECT 4,12 UNION ALL
SELECT 5,4 UNION ALL
SELECT 6,12 UNION ALL
SELECT 7,8
)
SELECT *,
CASE
WHEN ROW_NUMBER() OVER (PARTITION BY Match ORDER BY ID) = 1
THEN COUNT(*) OVER (PARTITION BY Match)
END AS Tot
FROM tableA
ORDER BY ID
SELECT match, COUNT(match ) as Tot
FROM tableA
GROUP BY match
Solution 1:
DECLARE #MyTable TABLE
(
ID INT PRIMARY KEY
,Match VARCHAR(10) NOT NULL
,Tot INT NULL
);
INSERT #MyTable(ID, Match)
SELECT 1, 123
UNION ALL
SELECT 2, 123
UNION ALL
SELECT 3, 12
UNION ALL
SELECT 4, 12
UNION ALL
SELECT 5, 4
UNION ALL
SELECT 6, 12
UNION ALL
SELECT 7, 8;
--SELECT
SELECT *
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a;
--UPDATE
WITH MyCTE
AS
(
SELECT a.Tot
,CASE
WHEN ROW_NUMBER()OVER(PARTITION BY a.Match ORDER BY a.ID ASC)=1
THEN COUNT(*)OVER(PARTITION BY a.Match)
END TotCalculated
FROM #MyTable a
)
UPDATE MyCTE
SET Tot = TotCalculated;
SELECT *
FROM #MyTable;
Solution 2:
UPDATE #MyTable
SET Tot = NULL;
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
ORDER BY x.ID
UPDATE #MyTable
SET Tot = t.Num
FROM #MyTable z
INNER JOIN
(
SELECT x.ID, y.Num
FROM
(
SELECT b.Match, MIN(b.ID) ID
FROM #MyTable b
GROUP BY b.Match
) x INNER JOIN
(
SELECT a.Match, COUNT(*) AS Num
FROM #MyTable a
GROUP BY a.Match
) y ON x.Match = y.Match
) t ON z.ID = t.ID;
SELECT *
FROM #MyTable;