Exclude columns with no data in them - sql

Using Oracle with TOAD.
I have a table that looks something like this (columns 2 and 4 are empty and columns 1, 3 and 5 have data in them):
column_1 column_2 column_3 column_4 column_5
a1 b1 c1
a2 b2 c2
a3 b3 c3
a4 b4 c4
I would like to do a simple select that excludes columns with no data in them (= columns 2 and 4) or ain other words, to select only the columns that have data in them.
Is there a select command such as SELECT * FROM test_table WHERE columns ARE NOT NULL (this is pseudo code just for clarification for my problem).
The result should look like this:
column_1 column_3 column_5
a1 b1 c1
a2 b2 c2
a3 b3 c3
a4 b4 c4

You have to do a three-step approach but it is largely tedious but do-able in sqlplus
1)First identify the columns which are empty
2)Define the headers without those columns
3)define the body without those columns
WITH data AS
(
SELECT '1' a,
'' b ,
2 c ,
'' d,
5 e
FROM dual
UNION ALL
SELECT '7' a,
'' b ,
2 c ,
'' d,
6
FROM dual
UNION ALL
SELECT '3' a,
'' b ,
3 c ,
'' d,
7
FROM dual
UNION ALL
SELECT '4' a,
'' b ,
3 c ,
'' d,
8
FROM dual
UNION ALL
SELECT '5' a,
'' b ,
2 c ,
'' d,
9
FROM dual),d1 AS
(
SELECT First_value(a) ignore nulls over (PARTITION BY a ORDER BY ROWNUM) ca,
first_value(b) ignore nulls over (PARTITION BY b ORDER BY ROWNUM) cb,
first_value(c) ignore nulls over (PARTITION BY c ORDER BY ROWNUM) cc,
first_value(d) ignore nulls over (PARTITION BY d ORDER BY ROWNUM) cd,
first_value(e) ignore nulls over (PARTITION BY e ORDER BY ROWNUM) ce
FROM data
WHERE ROWNUM=1 ),
d2 as (SELECT 0 rw,
CASE
WHEN ca IS NOT NULL THEN 'a'
ELSE ''
END
||chr(9)
||
CASE
WHEN cb IS NOT NULL THEN 'b'
ELSE ''
END
||chr(9)
||
CASE
WHEN cc IS NOT NULL THEN 'c'
ELSE ''
END
||chr(9)
||
CASE
WHEN cd IS NOT NULL THEN 'd'
ELSE ''
END
||chr(9)
||
CASE
WHEN ce IS NOT NULL THEN 'e'
ELSE ''
END as DATA1
FROM d1
UNION ALL
SELECT
rownum rw,
a
||chr(9)
||b
||chr(9)
||c
||chr(9)
||d
||chr(9)
||e
FROM data)
select /*ansiconsole*/ DATA1
from d2 order by rw asc;

Related

postgresql - count distinct combination of three columns- order doesn't matter

I'm trying to count distinct combinations of three columns, order of the columns doesn't matter
sample :
a a a
a a b
a b a
b b a
b a b
the result I'm getting :
a a a 1
a a b 1
a b a 1
b b a 1
b a b 1
desired result
aaa 1
aab 2
bba 2
You can use an ordered array
select v[1], v[2], v[3], count(*) n
from tbl t
cross join lateral (
select array_agg(col order by col) v
from (
values (c1),(c2),(c3)
) t(col)
) s
group by v[1], v[2], v[3];
db<>fiddle
Maybe you can use checksums for getting the required result eg if it is really just combinations 'a' and 'b' that you are dealing with, you could convert the letters to integers (by calling the ASCII() function) and add these up so that you get a checksum.
TABLE
create table t (c1, c2, c3 ) as
select 'a', 'a', 'a' union all
select 'a', 'a', 'b' union all
select 'a', 'b', 'a' union all
select 'b', 'b', 'a' union all
select 'b', 'a', 'b' ;
Checksums
select c1, c2, c3, ascii( c1 ) + ascii( c2 ) + ascii( c3 ) as checksum
from t ;
-- output
c1 c2 c3 checksum
a a a 291
a a b 292
a b a 292
b b a 293
b a b 293
If this works for you, then you can use window functions eg
select c1, c2, c3, rc_ as rowcount
from (
select c1, c2, c3
, count(*) over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rc_
, row_number() over ( partition by ascii( c1 ) + ascii( c2 ) + ascii( c3 ) order by 1 ) rn_
from t
) sq
where rc_ = rn_ ;
-- output
c1 c2 c3 rowcount
a a a 1
a b a 2
b a b 2
See dbfiddle.
If you are dealing with strings that cannot easily converted to integers, you could create a mapping between the strings and integers, and implement the map_ as a view (so that it is easy to use in subsequent queries) eg
MAP
-- {1} find all distinct elements
-- {2} map each element to an integer
create view map_
as
select val_, rank() over ( order by val_ ) weight_
from (
select distinct val_
from (
select distinct c1 val_ from t union all
select distinct c2 from t union all
select distinct c3 from t
) all_elements
) unique_elements ;
Once you have this map, you can use its values for creating checksums (maybe also in a view) ...
Checksums
create view t_checksums_
as
select c1, c2, c3, c1weight + c2weight + c3weight as checksum
from (
select
c1, ( select weight_ from map_ where c1 = map_.val_ ) c1weight
, c2, ( select weight_ from map_ where c2 = map_.val_ ) c2weight
, c3, ( select weight_ from map_ where c3 = map_.val_ ) c3weight
from t
) valandweight ;
... and then, you can use the same query as before, for obtaining the final result - see dbfiddle.

Join strings in Oracle like concat_ws in SQL Server

I have a table with multiple string columns I would like to join together with a separator.
c1
c2
c3
c4
a
b
c
d
a
b
a
The result for that should be
'a-b-c-d'
'a-b'
'a'
In SQL Server I just do
select concat_ws('-', c1, c2, c3, c4) from my_table
In Oracle I can do
SELECT COALESCE(c1, '') ||
CASE WHEN c2 IS NULL THEN '' ELSE '-' || c2 END ||
CASE WHEN c3 IS NULL THEN '' ELSE '-' || c3 END ||
CASE WHEN c4 IS NULL THEN '' ELSE '-' || c4 END
FROM my_table
Is there a better solution in Oracle or even one that works for both - SQL Server and Oracle?
A version that works in both Oracle and SQL Server is tricky because the only string concatenation function available is concat() with two arguments. But, you can do:
select trim('-' from
concat(coalesce(c1, ''),
concat(case when c2 is null then '' else concat('-', c2) end,
concat(case when c3 is null then '' else concat('-', c3) end,
case when c4 is null then '' else concat('-', c4) end
)
)
))
Here are the two db<>fiddles for SQL Server and Oracle.
select c1 || nvl2(c2, '-'||c2,c2) || nvl2(c3, '-'||c3,c3) || nvl2(c4, '-'||c4,c4)
from mytable
test it here
One option is to
concatenate all columns with - as a separator, and then
remove double (triple, ...) - signs (with regexp) and
remove leading/trailing - signs (with trim)
Something like this:
SQL> with test (c1, c2, c3, c4) as
2 (select 'a' , 'b' , 'c' , 'd' from dual union all
3 select 'a' , 'b' , null, null from dual union all
4 select 'a' , null, null, null from dual union all
5 select 'a' , null, 'c' , null from dual union all
6 select null, null, 'c' , 'd' from dual
7 )
8 select
9 c1, c2, c3, c4,
10 --
11 trim(both '-' from regexp_replace(c1 ||'-'|| c2 ||'-'|| c3 ||'-'|| c4, '-+', '-')) result
12 from test;
C1 C2 C3 C4 RESULT
-- -- -- -- --------------------
a b c d a-b-c-d
a b a-b
a a
a c a-c
c d c-d
SQL>

Get Distinct values without null

I have a table like this;
--Table_Name--
A | B | C
-----------------
A1 NULL NULL
A1 NULL NULL
A2 NULL NULL
NULL B1 NULL
NULL B2 NULL
NULL B3 NULL
NULL NULL C1
I want to get like this ;
--Table_Name--
A | B | C
-----------------
A1 B1 C1
A2 B2 NULL
NULL B3 NULL
How should I do that ?
Here's one option:
sample data is from line #1 - 9
the following CTEs (lines #11 - 13) fetch ranked distinct not null values from each column
the final query (line #15 onward) returns desired result by outer joining previous CTEs on ranked value
SQL> with test (a, b, c) as
2 (select 'A1', null, null from dual union all
3 select 'A1', null, null from dual union all
4 select 'A2', null, null from dual union all
5 select null, 'B1', null from dual union all
6 select null, 'B2', null from dual union all
7 select null, 'B3', null from dual union all
8 select null, null, 'C1' from dual
9 ),
10 --
11 ta as (select distinct a, dense_rank() over (order by a) rn from test where a is not null),
12 tb as (select distinct b, dense_rank() over (order by b) rn from test where b is not null),
13 tc as (select distinct c, dense_rank() over (order by c) rn from test where c is not null)
14 --
15 select ta.a, tb.b, tc.c
16 from ta full outer join tb on ta.rn = tb.rn
17 full outer join tc on ta.rn = tc.rn
18 order by a, b, c
19 /
A B C
-- -- --
A1 B1 C1
A2 B2
B3
SQL>
If you have only one value per column, then I think a simpler solution is to enumerate the values and aggregate:
select max(a) as a, max(b) as b, max(c) as c
from (select t.*,
dense_rank() over (partition by (case when a is null then 1 else 2 end),
(case when b is null then 1 else 2 end),
(case when c is null then 1 else 2 end)
order by a, b, c
) as seqnum
from t
) t
group by seqnum;
This only "aggregates" once and only uses one window function, so I think it should have better performance than handling each column individually.
Another approach is to use lateral joins which are available in Oracle 12C -- but this assumes that the types are compatible:
select max(case when which = 'a' then val end) as a,
max(case when which = 'b' then val end) as b,
max(case when which = 'c' then val end) as c
from (select which, val,
dense_rank() over (partition by which order by val) as seqnum
from t cross join lateral
(select 'a' as which, a as val from dual union all
select 'b', b from dual union all
select 'c', c from dual
) x
where val is not null
) t
group by seqnum;
The performance may be comparable, because the subquery removes so many rows.

MS SQL.Summation of various values across columns

Table T1 has 3 columns as C1, C2 and C3 having values as R, G, B
C1 C2 C3
R R R
R R R
G R R
G G R
B G B
B B B
I want a new table in the below structure:
R G B
9 4 5
In the above table, the distinct values of the Table T1 has to be displayed as the column name and the total count of the R, G, B values from the whole table has to be displayed.
Use the COUNT aggregation function on each column with a CASE expression to filter by the correct character value:
SQL Fiddle
Oracle and/or MS SQL Server Setup:
CREATE TABLE table_name(
C1 CHAR(1),
C2 CHAR(1),
C3 CHAR(1)
);
INSERT INTO table_name VALUES ( 'R', 'R', 'R' );
INSERT INTO table_name VALUES ( 'R', 'R', 'R' );
INSERT INTO table_name VALUES ( 'G', 'R', 'R' );
INSERT INTO table_name VALUES ( 'G', 'G', 'R' );
INSERT INTO table_name VALUES ( 'B', 'G', 'B' );
INSERT INTO table_name VALUES ( 'B', 'B', 'B' );
Query 1:
SELECT COUNT( CASE C1 WHEN 'R' THEN 1 END )
+ COUNT( CASE C2 WHEN 'R' THEN 1 END )
+ COUNT( CASE C3 WHEN 'R' THEN 1 END ) AS R,
COUNT( CASE C1 WHEN 'G' THEN 1 END )
+ COUNT( CASE C2 WHEN 'G' THEN 1 END )
+ COUNT( CASE C3 WHEN 'G' THEN 1 END ) AS G,
COUNT( CASE C1 WHEN 'B' THEN 1 END )
+ COUNT( CASE C2 WHEN 'B' THEN 1 END )
+ COUNT( CASE C3 WHEN 'B' THEN 1 END ) AS B
FROM table_name
Results:
| R | G | B |
|---|---|---|
| 9 | 4 | 5 |
Query 2 or you can use UNPIVOT:
SELECT COUNT( CASE value WHEN 'R' THEN 1 END ) AS R,
COUNT( CASE value WHEN 'G' THEN 1 END ) AS G,
COUNT( CASE value WHEN 'B' THEN 1 END ) AS B
FROM table_name
UNPIVOT ( value FOR id IN ( C1, C2, C3 ) ) AS u -- Do not need AS keyword in Oracle
Results:
| R | G | B |
|---|---|---|
| 9 | 4 | 5 |

Select only rows that have unique fields

What is an SQL command that checks for rows that have rows with no duplicate fields in them.
ex:
A A A B B B should not be in the resulting table.
Only rows such as A B C D E F
i.e. given data like:
A A A B B B
A B C D E F
A A B G H Q
Should return A B C D E F
There is no simple command to do this.
is seems an unusual requirement and possibly an indication that the table is not in first normal form if all columns are interchangeable.
The following works in Microsoft SQL Server
;With YourData AS
(
select 'A' as C1, 'A' as C2, 'A' as C3, 'B' as C4, 'B' as C5, 'B' as C6 UNION ALL
select 'A' as C1, 'B' as C2, 'C' as C3, 'D' as C4, 'E' as C5, 'F' as C6
)
SELECT *
FROM YourData
WHERE 1 =
( SELECT TOP 1 COUNT(*) AS Cnt
FROM (
SELECT C1 AS C
UNION ALL
SELECT C2
UNION ALL
SELECT C3
UNION ALL
SELECT C4
UNION ALL
SELECT C5
UNION ALL
SELECT C6
) D
GROUP BY C
ORDER BY Cnt DESC
)
Select distinc * returns unique ROWS not unique values from fields.
You should compare each column's value with others. (Assuming column types are the same). For example, for a 4 column table you should do smoething like:
SELECT Col1, Col2, Col3, Col4 FROM MyTable WHERE
Col1 NOT IN (Col2,Col3,Col4) AND
Col2 NOT IN (Col3,Col4) AND
Col3 <> Col4
SELECT DISTINCT * FROM tablename
SELECT DISTINCT col FROM tabl
SELECT * FROM
mytable
WHERE mytable.col1 != mytable.col2 != mytable.col3 ...