This really is a formatting issue which is best handled by the client. For instance, in SQL*Plus we can use BREAK
....
SQL> select t1.*, t2.* from t1, t2
2 /
A B C D C1
--- --- --- --- ----------
aaa bbb ccc ddd 111
aaa bbb ccc ddd 222
SQL> break on a on b on c on d
SQL> select t1.*, t2.* from t1, t2
2 /
A B C D C1
--- --- --- --- ----------
aaa bbb ccc ddd 111
222
SQL>
Note: in the absence of any further information I opted for a Cartesian product.
edit
BREAK
is a SQL*Plus command, which suppresses duplicate columns in our rows. It only works in the SQL*Plus client. As might be expected, it is covered in Oracle's SQL*Plus User Guide. Find out more.
I used BREAK
as an example of the proper way of doing things, because it is clean and correctly implements the separation of concerns. It you are using a different client you would need to use its formatting capabilities. It is possible to tweak the SQL (see below) but that diminishes the utility of the query, because we cannot reuse the query in other places which don't want to suppress the duplicated values.
Anyway, here is one solution which uses the ROW_NUMBER()
analytic function in an inline view.
SQL> select * from t1
2 /
A B C D ID
--- --- --- --- ----------
eee fff ggg hhh 1
aaa bbb ccc ddd 2
SQL> select * from t2
2 /
C1 ID
---------- ----------
333 2
111 1
222 2
444 2
SQL> select t1_id
2 , case when rn = 1 then a else null end as a
3 , t2_id
4 , c1
5 from (
6 select t1.id as t1_id
7 , row_number () over (partition by t1.id order by t2.c1) as rn
8 , t1.a
9 , t2.c1
10 , t2.id as t2_id
11 from t1, t2
12 where t1.id = t2.id
13 )
14 order by t1_id, rn
15 /
T1_ID A T2_ID C1
---------- --- ---------- ----------
1 eee 1 111
2 aaa 2 222
2 2 333
2 2 444
SQL>
I chose not to use LAG()
, because that only works with fixed offsets, and it seemed likely that the number of rows in T2 would be variable.