How to mix two results in SQL (example included) - sql

I'm trying to figure out a way to make a mix of data this way:
Table A:
1
2
3
4
5
Table B:
a
b
c
d
e
Mix into:
1
a
2
b
3
c
4
d
5
e

This is a sorting problem:
select col
from ((select col, 1 as which, row_number() over (order by col) as seqnum
from a
) union all
(select col, 2 as which, row_number() over (order by col) as seqnum
from b
)
) ab
order by seqnum, which

Use UNION ALL with ROW_NUMBER():
SELECT mycol FROM (
SELECT 1 seq, mycolA AS mycol, ROW_NUMBER() OVER(ORDER BY mycolA) AS rn
FROM tableA
UNION ALL
SELECT 2, mycolB, ROW_NUMBER() OVER(ORDER BY mycolB)
FROM tableB
) x
ORDER BY rn, seq

Related

How to group the same values which is in sequence order

I'm trying to group data in sequence order. I have the following table:
id num
-------
1 1
2 1
3 1
4 2
5 1
6 2
7 2
8 4
9 4
10 4
I need the SQL query to output the following:
num count(num)
-------------------
1 3
2 1
1 1
2 2
4 3
Sample data:
select * into #temp
from (
select 1 as id, 1 as num union all
select 2, 1 union all
select 3, 1 union all
select 4, 2 union all
select 5, 1 union all
select 6, 2 union all
select 7, 2 union all
select 8, 4 union all
select 9, 4 union all
select 10, 4
) as abc
select * from #temp
select num, count(num)
from #temp
group by num
I need this :
num count(num)
-------------------
1 3
2 1
1 1
2 2
4 3
The actual output :
num count(num)
---------------------
1 4
2 3
4 3
This is a gaps and islands problem. Here is one way to solve it using lag() and a cumulative sum():
select min(num) num, count(*) count_num
from (
select t.*, sum(case when num = lag_num then 0 else 1 end) over(order by id) grp
from (
select t.*, lag(num) over(order by id) lag_num
from #temp t
) t
) t
group by grp
Demo on DB Fiddlde:
num | count_num
--: | --------:
1 | 3
2 | 1
1 | 1
2 | 2
3 | 3
Another Approach can be using row_number
select num, count(*)
from (select t.*,
(row_number() over (order by id) -
row_number() over (partition by num order by id)
) as grp
from #temp t
) t
group by grp, num;
DBFIDDLE
Gaps and islands problems are fun because there are so many different ways to address them. Here is one approach that does not require aggregation -- although it does require more use of window functions.
This is possible because the only information you are requesting is the count. If the id has no gaps and is sequential:
select num,
lead(id, 1, max_id + 1) over (order by id) - id
from (select t.*,
lag(num) over (order by id) as prev_num,
max(id) over () as max_id
from temp t
) t
where prev_num is null or prev_num <> num
order by id;
Otherwise, you can generate such a sequence easily:
select num,
lead(seqnum, 1, cnt + 1) over (order by id) - seqnum
from (select t.*,
lag(num) over (order by id) as prev_num,
row_number() over (order by id) as seqnum,
count(*) over () as cnt
from temp t
) t
where prev_num is null or prev_num <> num
order by id;
Here is a db<>fiddle.

Rows Columns Traverse

I have data in the below format
id idnew
1 2
3 4
2
4 7
6 8
7
Result Should be something like this
ID should be followed by idnew
1
2
3
4
2
4
7
6
8
7
Thanks in advance
This should maintain the order:
SELECT id
FROM (
SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS RowNumber
FROM myTable
UNION ALL
SELECT idnew, ROW_NUMBER() OVER (ORDER BY idnew) +
(SELECT COUNT(*) FROM dbo.myTable) AS RowNumber
FROM myTable
WHERE idnew IS NOT NULL
) a
ORDER BY RowNumber
I am assuming the id column is NOT NULL-able.
NOTE: If you want to keep the NULL values from the idnew column AND maintain the order, then remove the WHERE clause and ORDER BY id in the second select:
SELECT id
FROM (
SELECT id, ROW_NUMBER() OVER (ORDER BY id) AS RowNumber
FROM myTable
UNION ALL
SELECT idnew, ROW_NUMBER() OVER (ORDER BY id) +
(SELECT COUNT(*) FROM dbo.myTable) AS RowNumber
FROM myTable
) a
ORDER BY RowNumber
This is fully tested, try it here: https://rextester.com/DVZXO21058
Setting up the table as you described:
CREATE TABLE myTable (id INT, idnew INT);
INSERT INTO myTable (id, idnew)
VALUES (1, 2),
(3, 4),
(2, NULL),
(4, 7),
(6, 8),
(7, NULL);
SELECT * FROM myTable;
Here is the query to do the trick:
SELECT mixed_id FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS row_num,
id,
idnew
FROM myTable
) AS x
UNPIVOT
(
mixed_id for item in (id, idnew)
) AS y
WHERE mixed_id IS NOT NULL
ORDER BY row_num, mixed_id;
In order not to further complicate the query, this is taking advantage of 'id' would rank ahead of 'idnew' as a string. I believe string ranking is not the key issue here.
Using Cross Apply
;WITH CTE (id,idnew)
AS
(
SELECT 1,2 UNION ALL
SELECT 3,4 UNION ALL
SELECT 2,NULL UNION ALL
SELECT 4,7 UNION ALL
SELECT 6,8 UNION ALL
SELECT 7,NULL
)
SELECT New
FROM CTE
CROSS APPLY ( VALUES (id),(idnew))AS Dt (New)
WHERE dt.New IS NOT NULL
Result
New
---
1
2
3
4
2
4
7
6
8
7

create one list from to two columns

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

How to map the 2 different record set one by one?

Let' say I have 2 sql queries. Table A contains,
ID
--
1
1
1
2
3
4
This query,
Select distinct ID1 FROM A
gives me,
ID
--
1
2
3
4
Second one
Select ID2 FROM B
which gives me,
ID2
--
8
21
33
43
How 2 get this record set?
ID1 ID2
--- ---
1 8
2 21
3 33
4 43
You did not specify what version of sql server but if you are using sql server 2008+, one way that you can do this is by adding the row_number() to each table and then joining on the row_number():
select a.id, b.id2
from
(
select id, row_number() over(order by id) rn
from a
) a
inner join
(
select id2, row_number() over(order by id2) rn
from b
) b
on a.rn = b.rn
See SQL Fiddle with Demo
If you want to only use DISTINCT values, then you should be able to use:
select a.id, b.id2
from
(
select id, row_number() over(order by id) rn
from
(
select distinct id
from a
) a
) a
inner join
(
select id2, row_number() over(order by id2) rn
from b
) b
on a.rn = b.rn;
See SQL Fiddle with Demo
If you have a different number of rows in each table, then you might want to use a FULL OUTER JOIN:
select a.id, b.id2
from
(
select id, row_number() over(order by id) rn
from
(
select distinct id
from a
) a
) a
full outer join
(
select id2, row_number() over(order by id2) rn
from b
) b
on a.rn = b.rn;
See SQL Fiddle with Demo

SQL filtering with COALESCE or window function

I have a table with the following simplified data:
PK FK Type
----------------
1 11 A
2 11 B
3 12 B
4 13 C
5 13 D
6 14 D
And I want a result set of:
PK FK Type
---------------
1 11 A
3 12 B
4 13 C
6 14 D
So if a given FK value has a Type A, don't give me any rows with B, C, or D. Or if the value has a Type B, then filter out C and D, etc. This feels like I need to apply a window function or coalesce, but I'm not sure how to do it.
Use:
SELECT x.*
FROM (SELECT s.*,
ROW_NUMBER() OVER(PARTITION BY s.fk
ORDER BY s.pk) rnk
FROM YOUR_TABLE s) x
WHERE x.rnk = 1
...or using CTE (no performance difference):
WITH example AS (
SELECT s.*,
ROW_NUMBER() OVER(PARTITION BY s.fk
ORDER BY s.pk) rnk
FROM sample s)
SELECT x.*
FROM example x
WHERE x.rnk = 1
Proof:
WITH sample AS (
SELECT 1 AS PK, 11 AS FK, 'A' AS [TYPE]
UNION ALL
SELECT 2 AS PK, 11 AS FK, 'B' AS [TYPE]
UNION ALL
SELECT 3 AS PK, 12 AS FK, 'B' AS [TYPE]
UNION ALL
SELECT 4 AS PK, 13 AS FK, 'C' AS [TYPE]
UNION ALL
SELECT 5 AS PK, 13 AS FK, 'D' AS [TYPE]
UNION ALL
SELECT 6 AS PK, 14 AS FK, 'D' AS [TYPE])
SELECT x.*
FROM (SELECT s.*, ROW_NUMBER() OVER(PARTITION BY s.fk
ORDER BY s.pk) rnk
FROM sample s) x
WHERE x.rnk = 1
Result:
pk fk type
----------------
1 11 A
3 12 B
4 13 C
6 14 D
So, for each FK value, you want the minimal PK value that matches, and the corresponding type for the PK? As ever, do it in steps.
Which are the PK values we want?
SELECT FK, MIN(PK) AS PK
FROM SimplifiedData
GROUP BY FK
How to get the corresponding rows - why, join with the main table, of course:
SELECT S.PK, S.FK, S.Type
FROM SimplifiedData AS S
JOIN (SELECT FK, MIN(PK) AS PK
FROM SimplifiedData
GROUP BY FK
) AS T ON S.PK = T.PK
ORDER BY S.FK;