Concatenate values from multiple columns in Oracle - sql

I'm working with an oracle database and what I basically need to concatenate in one column, values from multiple columns for every row.
Something like this:
col1 col2 col3 col4 col5
____________________________________
1 A B C D
2 A B C
3 C A
4 D A C
col1 col2
____________
1 A,B,C,D
2 A,B,C
3 C,A
4 D,A,C

SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE test (col1, col2, col3, col4, col5 ) AS
SELECT 1, 'A', 'B', 'C', 'D' FROM DUAL
UNION ALL SELECT 2, 'A', 'B', 'C', NULL FROM DUAL
UNION ALL SELECT 3, 'C', 'A', NULL, NULL FROM DUAL
UNION ALL SELECT 4, 'D', 'A', 'C', NULL FROM DUAL
UNION ALL SELECT 5, NULL, NULL, NULL, NULL FROM DUAL
UNION ALL SELECT 6, NULL, NULL, NULL, 'A' FROM DUAL
UNION ALL SELECT 7, 'B', NULL, NULL, 'A' FROM DUAL
UNION ALL SELECT 8, NULL, 'C', NULL, 'A' FROM DUAL;
Query 1:
If there are no NULL values between other values (it will introduce multiple commas in rows 7 & 8):
SELECT col1,
TRIM( ',' FROM col2||','||col3||','||col4||','||col5 ) AS col2
FROM test
Results:
| COL1 | COL2 |
|------|---------|
| 1 | A,B,C,D |
| 2 | A,B,C |
| 3 | C,A |
| 4 | D,A,C |
| 5 | (null) |
| 6 | A |
| 7 | B,,,A |
| 8 | C,,A |
The last two queries will work for all examples:
Query 2:
SELECT col1,
TRIM( ',' FROM col2 || NVL2( col3, ','||col3, NULL ) || NVL2( col4, ','||col4, NULL ) || NVL2( col5, ','||col5, NULL ) ) AS col2
FROM test
Results:
| COL1 | COL2 |
|------|---------|
| 1 | A,B,C,D |
| 2 | A,B,C |
| 3 | C,A |
| 4 | D,A,C |
| 5 | (null) |
| 6 | A |
| 7 | B,A |
| 8 | C,A |
Query 3:
SELECT col1,
REGEXP_REPLACE( col2||','||col3||','||col4||','||col5, '(^|,),+|,+($)', '\1' ) AS col2
FROM test
Results:
| COL1 | COL2 |
|------|---------|
| 1 | A,B,C,D |
| 2 | A,B,C |
| 3 | C,A, |
| 4 | D,A,C |
| 5 | (null) |
| 6 | A |
| 7 | B,A |
| 8 | C,A |

Use below query
select col1,rtrim( col2||','||col3||','||col4||','||col5,' ,') as col2 from table_name

there's probably a better way of handling the trailing comma, but this should work
SELECT col1,
col2 || decode(col3,'','',',') || col3 || decode(col4,'','',',') || col4 || decode(col5,'','',',')|| col5 as "col2"
FROM table

Use concatenation ||
Select Col1|| ',' ||Col2|| ',' ||Col2|| ',' ||Col3|| ',' || Col4 As OneCol
From Table;

Related

Create View with reverse Row values SQL

I have data like
name| col1 | col2 | col3 | col4 | col4 | col5 |
rv | rv1 | rv2 | rv3 | rv4 | | |
sgh | sgh1 | sgh2 | | | | |
vik | vik1 | vik2 | vik3 | vik4 |vik5 |vik6 |
shv | shv1 | shv2 | shv3 | shv4 |shv5 | |
Table Name: emp_data
to create View to get DATA like
name| col1 | col2 | col3 | col4 | col4 | col5 |
rv | rv4 | rv3 | rv2 | rv1 | | |
sgh | sgh2 | sgh1 | | | | |
vik | vik6 | vik5 | vik4 | vik3 |vik2 |vik1 |
shv | shv5 | shv4 | shv3 | shv2 |shv1 | |
MySql 8 supports LATERAL, this way you can sort values by positions and conditionally aggregate them back.
with tbl(name, col1, col2, col3 ,col4 ,col5 , col6) as
(
select 'rv ','rv1 ','rv2 ','rv3 ','rv4 ',null,null union all
select 'sgh','sgh1','sgh2', null,null,null,null union all
select 'vik','vik1','vik2','vik3','vik4','vik5','vik6' union all
select 'shv','shv1','shv2','shv3','shv4','shv5', null
)
select tbl.name, t.*
from tbl
, lateral (
select
max(case n when 1 then val end) col1,
max(case n when 2 then val end) col2,
max(case n when 3 then val end) col3,
max(case n when 4 then val end) col4,
max(case n when 5 then val end) col5,
max(case n when 6 then val end) col6
from (
select row_number() over( order by n) n, val
from (
select case when col1 is null then 99 else 6 end n, col1 val union all
select case when col2 is null then 99 else 5 end n, col2 val union all
select case when col3 is null then 99 else 4 end n, col3 val union all
select case when col4 is null then 99 else 3 end n, col4 val union all
select case when col5 is null then 99 else 2 end n, col5 val union all
select case when col6 is null then 99 else 1 end n, col6 val
) t
) t
) t
db<>fidle

Filtering a table via another table's values

I have 2 tables:
Value
+----+-------+
| id | name |
+----+-------+
| 1 | Peter |
| 2 | Jane |
| 3 | Joe |
+----+-------+
Filter
+----+---------+------+
| id | valueid | type |
+----+---------+------+
| 1 | 1 | A |
| 2 | 1 | B |
| 3 | 1 | C |
| 4 | 1 | D |
| 5 | 2 | A |
| 6 | 2 | C |
| 7 | 2 | E |
| 8 | 3 | A |
| 9 | 3 | D |
+----+---------+------+
I need to retrieve the values from the Value table where the related Filter table does not contain the type 'B' or 'C'
So in this quick example this would be only Joe.
Please note this is a DB2 DB and i have limited permissions to run selects only.
Or also a NOT IN (<*fullselect*) predicate:
Only that my result is 'Joe', not 'Jane' - and the data constellation would point to that ...
WITH
-- your input, sans reserved words
val(id,nam) AS (
SELECT 1,'Peter' FROM sysibm.sysdummy1
UNION ALL SELECT 2,'Jane' FROM sysibm.sysdummy1
UNION ALL SELECT 3,'Joe' FROM sysibm.sysdummy1
)
,
filtr(id,valueid,typ) AS (
SELECT 1,1,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 2,1,'B' FROM sysibm.sysdummy1
UNION ALL SELECT 3,1,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 4,1,'D' FROM sysibm.sysdummy1
UNION ALL SELECT 5,2,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 6,2,'C' FROM sysibm.sysdummy1
UNION ALL SELECT 7,2,'E' FROM sysibm.sysdummy1
UNION ALL SELECT 8,3,'A' FROM sysibm.sysdummy1
UNION ALL SELECT 9,3,'D' FROM sysibm.sysdummy1
)
-- real query starts here
SELECT
*
FROM val
WHERE id NOT IN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
)
;
-- out id | nam
-- out ----+-------
-- out 3 | Joe
Or also, a failing left join:
SELECT
val.*
FROM val
LEFT JOIN (
SELECT valueid FROM filtr WHERE typ IN ('B','C')
) filtr
ON filtr.valueid = val.id
WHERE valueid IS NULL
You can use EXISTS, as in:
select *
from value v
where not exists (
select null from filter f
where f.valueid = v.id and f.type in ('B', 'C')
);
Result:
ID NAME
--- -----
3 Joe
See running example at db<>fiddle.

How do I select non null unequal values across columns?

I would like to select records where values across 5 columns, if not null, are unequal. Any number of columns can be non null.
schema:
given table
**Schema (SQLite v3.30)**
CREATE TABLE test (
id INT,
col1 text,
col2 text,
col3 text,
col4 text,
col5 text
);
INSERT INTO test (id, col1, col2, col3, col4, col5) VALUES (1, 'EA', 'EA', null, null, null);
INSERT INTO test (id, col1, col2, col3, col4, col5) VALUES (2, 'EA', 'MT', null, null, null);
INSERT INTO test (id, col1, col2, col3, col4, col5) VALUES (3, null, 'EA', null, 'KG', null);
INSERT INTO test (id, col1, col2, col3, col4, col5) VALUES (4, null, null, 'KG', 'MT', 'AB');
INSERT INTO test (id, col1, col2, col3, col4, col5) VALUES (5, null, null, 'EA', 'MT', 'EA');
---
**Query #1**
SELECT * FROM test;
| id | col1 | col2 | col3 | col4 | col5 |
| --- | ---- | ---- | ---- | ---- | ---- |
| 1 | EA | EA | | | |
| 2 | EA | MT | | | |
| 3 | | EA | | KG | |
| 4 | | | KG | MT | AB |
| 5 | | | EA | MT | EA |
---
Desired output
| id | col1 | col2 | col3 | col4 | col5 |
| --- | ---- | ---- | ---- | ---- | ---- |
| 2 | EA | MT | | | |
| 3 | | EA | | KG | |
| 4 | | | KG | MT | AB |
| 5 | | | EA | MT | EA |
EDIT: To clarify, id=5 is included in the output since not all values are the same. When I stated values across all columns are unequal, I meant if if any value is distinct from the rest of the non null values. To state it differently, I require records which have at least 1 non null value different from the other non null value.
Hmmm . . . this is tricky. Assuming that the empty values are NULL and no values are blank and you want all distinct values, you can do:
select t.*
from test t
where (col1 is null or col1 not in (coalesce(col2, ''), coalesce(col3, ''), coalesce(col4, ''), coalesce(col5, ''))) and
(col2 is null or col2 not in (coalesce(col3, ''), coalesce(col4, ''), coalesce(col5, ''))) and
(col3 is null or col3 not in (coalesce(col4, ''), coalesce(col5, ''))) and
(col4 is null or col4 not in (coalesce(col5, '')))
EDIT:
If you just want more than one distinct value, then:
select t.*
from test t
where col1 not in (coalesce(col2, ''), coalesce(col3, ''), coalesce(col4, ''), coalesce(col5, '')) or
col2 not in (coalesce(col1, ''), coalesce(col3, ''), coalesce(col4, ''), coalesce(col5, '')) or
col3 not in (coalesce(col1, ''), coalesce(col2, ''), coalesce(col4, ''), coalesce(col5, '')) or
col4 not in (coalesce(col1, ''), coalesce(col2, ''), coalesce(col3, ''), coalesce(col5, ''))
Here is a db<>fiddle.
Note: This does not return rows with only one value which technically do not meet your specifications.
You can aggregate on the union of each id with 1 of the columns and set the condition in the having clause:
with cte as (
select id from (
select id, col1 col from test union all
select id, col2 from test union all
select id, col3 from test union all
select id, col4 from test union all
select id, col5 from test
)
where col is not null
group by id
having count(distinct col) > 1
)
select * from test where id in cte
See the demo.
Results:
> id | col1 | col2 | col3 | col4 | col5
> -: | :--- | :--- | :--- | :--- | :---
> 2 | EA | MT | null | null | null
> 3 | null | EA | null | KG | null
> 4 | null | null | KG | MT | AB
> 5 | null | null | EA | MT | EA

Translate table values to text following a fixed pattern

We use software to store combinations of financial elements. Those elements are allowed in certain combinations. Exceptions of these combinations are SQL-like statements in the front-end, and are saved as numerical values in a database table like the following example:
+------+------+------+------+------+
| Col1 | Col2 | Col3 | Col4 | Col5 |
+------+------+------+------+------+
| 1 | 2 | 4 | 5 | 1 |
+------+------+------+------+------+
| -1 | 2 | 6 | 4 | 5 |
+------+------+------+------+------+
| 1 | 2 | 5 | 7 | 1 |
+------+------+------+------+------+
I would like to translate those numerical values back to a SQL-statement like the following example:
+------+-----------+------+-----------+------+-----------+------+-----------+------+-----------+
| Col1 | Col1Trans | Col2 | Col2Trans | Col3 | Col3Trans | Col4 | Col4Trans | Col5 | Col5Trans |
+------+-----------+------+-----------+------+-----------+------+-----------+------+-----------+
| 1 | ( | 2 | SELECT | 4 | CODE | 5 | LIKE | 1 | * |
+------+-----------+------+-----------+------+-----------+------+-----------+------+-----------+
| -1 | | 2 | SELECT | 6 | NUMBER | 4 | = | 5 | AND |
+------+-----------+------+-----------+------+-----------+------+-----------+------+-----------+
| 1 | ( | 2 | SELECT | 5 | TOOL | 7 | <> | 1 | * |
+------+-----------+------+-----------+------+-----------+------+-----------+------+-----------+
The numerical values differ in each column so I can only imagine the use of a lot of case...when statements which I doubt will be efficiƫnt. I don't want to create tables to hold the translation values. Are there ways to do this with arrays?
Are there any code samples to easily loop through table/columns and translate the contents of it?
You can use below code and add more case statement as per the requirement.
SELECT Col1
,CASE
WHEN Col1 = 1 THEN '('
ELSE '' END AS Col1Trans
,Col2
,CASE
WHEN Col2 = 2 THEN 'SELECT'
END AS Col2Trans
,Col3
,CASE
WHEN Col3 = 4 THEN 'CODE'
WHEN Col3 = 6 THEN 'NUMBER'
WHEN Col3 = 5 THEN 'TOOL'
END AS Col3Trans
,Col4
,CASE
WHEN Col4 = 5 THEN 'LIKE'
WHEN Col4 = 4 THEN '='
WHEN Col4 = 7 THEN '<>'
END AS Col4Trans
,Col5
,CASE
WHEN Col5 = 1 THEN '*'
WHEN Col5 = 5 THEN 'AND'
END AS Col5Trans
The best way to avoid so many case when and decode and etc is to use with as clause as following:
With col1trans (value, translation) as
(Select 1, '(' from dual union all
Select -1, null from dual),
Col2trans (value, translation) as
(Select 2, 'SELECT' from dual)
..
... till col5trans
Select m.col1, t1.translation as col1trans,
.... till m.col5, t5.translation
From your_table m join col1trans t1 m.col1=t1.value
join col2trans t2 m.col2=t2.value
... till col5trans
Cheers!!

group by conditional on two columns in hibernate

I want to group by on two columns. I want to get total of c group by a and b if b is not null and group by a if b is null
I wrote this query but it does not work in case b is null!the result of query is all rows that b is not null
select m.s.a ,
case when (m.l is not null)
then m.l.code end , coalesce(sum(m.c),0 )
from material m where m.Item.id =:itemId
group by m.s.a, case
when (m.l is not null)
then m.l.code end
+--+----+-------+---+
| | s | l | c |
+--+----+-------+---+
| | a | d | 1 |
| | a | d | 9 |
| | a | e | 3 |
| | a | f | 4 |
| | c | g | 5 |
| | c | g | 6 |
| | c | h | 20 |
| | d | null | 7 |
| | d | null | 8 |
result expected:
+--+----+-------+---+
| | s | l | c |
+--+----+-------+---+
| | a | d | 10 |
| | a | e | 3 |
| | a | f | 4 |
| | c | g | 11 |
| | c | h | 20 |
| | d | | 15 |
By default, oracle/postgres/mysql will produces the expected output.
SELECT s,l,sum(c)
FROM temp
GROUP BY s,l;
If you don't want to group by NULL values you can use UNION
SELECT s,l,sum(c)
FROM temp
WHERE l is NOT NULL
GROUP BY s,l
UNION
SELECT s,l,sum(c)
FROM temp
WHERE l is NULL;
with data (col1, col2, val) as
(
select 'a', 'd', 1 from dual union all
select 'a', 'd', 9 from dual union all
select 'a', 'e', 3 from dual union all
select 'a', 'f', 4 from dual union all
select 'c', 'g', 5 from dual union all
select 'c', 'g', 6 from dual union all
select 'c', 'h', 20 from dual union all
select 'd', null, 7 from dual union all
select 'd', null, 8 from dual union all
select 'e', 'g', null from dual -- additional check if val is null
)
,
prs (col1, col2, col1n2) as
(
select distinct col1, col2, col1||'-'||col2 from data
)
,
rs (col, val) as
(
-- concatenate the columns that need to be grouped by
-- to act as one single column (col1 and col2)
select col1||'-'||col2, sum(nvl(val,0)) from data group by col1||'-'||col2
)
select
prs.col1, prs.col2, rs.val
from
rs join prs
on (prs.col1n2 = rs.col)
order by 1
;