How can I combine 3 columns into one - sql

I have 3 columns called product_one, product_two and product_three and I want to combine all of them into one.
From this:
|product_one | product_two | product_three |
|------------------------------------------|
|spoon | phone | knife
|fork | case |
To this:
|products|
|--------
|spoon |
|fork |
|phone |
|case |
|knife |
How is this possible in sql?

Consider below approach
select products from your_table
unpivot (products for col in (product_one, product_two, product_three))
if applied to sample data in your question - output is
Another option that does not require specifying column names:
select split(kv, ':')[offset(1)] products
from your_table t,
unnest(split(translate(to_json_string(t), '{}"', ''))) kv
where split(kv, ':')[offset(1)] != 'null'
Yet another one -
select trim(value) products
from your_table t,
unnest(split(trim(format('%t', t), '()'))) value
where trim(value) != 'NULL'
all with same output

select t.product_one
from your_table t
union all
select t2.product_two
from your_table t2
union all
select t3.product_three
from your_table t3

By union:
select a.product_one as pr from a where a.product_one is not null
union
select b.product_two as pr from b where b.product_two is not null
union
select c.product_three as pr from c where c.product_three is not null

Related

Transforming columns to row values

Have a table in Google Bigquery like this with 1 id column (customers) and 3 store-name columns:
id |PA|PB|Mall|
----|--|--|----|
3699|1 |1 | 1 |
1017| |1 | 1 |
9991|1 | | |
My objective is to have the option to select customers (id's) who visited for example:
ONLY PA
PA and PB
PA and Mall
PA, PB and Mall
One alternative output could be:
id |Store |
----|--------- |
3699|PA+PB+Mall|
1017|PB+Mall |
9991|PA |
However this would not give me counts of all stopping by PA regardless of other stores visited. In the example above that count would have been 2 (3699 and 9991).
A second alternative output could be:
id |Store|
----|-----|
3699|PA |
3699|PB |
3699|Mall |
1017|PB |
1017|Mall |
9991|PA |
However, this would not allow me (i think) to select/filter those who has visited for example BOTH PA and Mall (only 3699)
A third alternative output could be a combo:
id |Store| Multiple store|
----|-----|---------------|
3699|PA | PA+PB+Mall |
3699|PB | PA+PB+Mall |
3699|Mall | PA+PB+Mall |
1017|PB | PB+Mall |
1017|Mall | PB+Mall |
9991|PA | |
What option is the best and is there any other alternatives to achieve my objective? I believe alternative 3 could be best, but not sure how to achieve it.
It depends what you want. For instance, the third would simply be:
select t.*,
string_agg(store, '+') over (partition by id)
from t;
The second would be:
select id, string_agg(store, '+')
from t
group by id;
For the third option, you may try unpivoting your current table, then applying STRING_AGG to get the computed column containing all stores for each id:
WITH cte AS (
SELECT id, CASE WHEN PA = 1 THEN 'PA' END AS Store
FROM yourTable
UNION ALL
SELECT id, CASE WHEN PB = 1 THEN 'PB' END
FROM yourTable
UNION ALL
SELECT id, CASE WHEN Mall = 1 THEN 'Mall' END
FROM yourTable
)
SELECT id, Store,
STRING_AGG(store, '+') OVER (PARTITION BY id) All_Stores
FROM cte
WHERE Store IS NOT NULL
ORDER BY id, Store;
Consider below approaches to all three options
Assuming input data is filled with nulls when it is empty in question's sample
with `project.dataset.table` as (
select 3699 id, 1 PA, 1 PB, 1 Mall union all
select 1017, null, 1, 1 union all
select 9991, 1, null, null
)
Option #1
select id, string_agg(key, '+') as Store
from `project.dataset.table` t,
unnest(split(translate(to_json_string(t), '{}"', ''))) kv,
unnest([struct(split(kv,':')[offset(0)] as key, split(kv,':')[offset(1)] as value)])
where key !='id'
and value != 'null'
group by id
with output
Option #2
select id, key as Store
from `project.dataset.table` t,
unnest(split(translate(to_json_string(t), '{}"', ''))) kv,
unnest([struct(split(kv,':')[offset(0)] as key, split(kv,':')[offset(1)] as value)])
where key !='id'
and value != 'null'
with output
Option #3
select id, key as Store,
string_agg(key, '+') over(partition by id) as Multiple_Store
from `project.dataset.table` t,
unnest(split(translate(to_json_string(t), '{}"', ''))) kv,
unnest([struct(split(kv,':')[offset(0)] as key, split(kv,':')[offset(1)] as value)])
where key !='id'
and value != 'null'
with output

Redshift create all the combinations of any length for the values in one column

How can we create all the combinations of any length for the values in one column and return the distinct count of another column for that combination?
Table:
+------+--------+
| Type | Name |
+------+--------+
| A | Tom |
| A | Ben |
| B | Ben |
| B | Justin |
| C | Ben |
+------+--------+
Output Table:
+-------------+-------+
| Combination | Count |
+-------------+-------+
| A | 2 |
| B | 2 |
| C | 1 |
| AB | 3 |
| BC | 2 |
| AC | 2 |
| ABC | 3 |
+-------------+-------+
When the combination is only A, there are Tom and Ben so it's 2.
When the combination is only B, 2 distinct names so it's 2.
When the combination is A and B, 3 distinct names: Tom, Ben, Justin so it's 3.
I'm working in Amazon Redshift. Thank you!
NOTE: This answers the original version of the question which was tagged Postgres.
You can generate all combinations with this code
with recursive td as (
select distinct type
from t
),
cte as (
select td.type, td.type as lasttype, 1 as len
from td
union all
select cte.type || t.type, t.type as lasttype, cte.len + 1
from cte join
t
on 1=1 and t.type > cte.lasttype
)
You can then use this in a join:
with recursive t as (
select *
from (values ('a'), ('b'), ('c'), ('d')) v(c)
),
cte as (
select t.c, t.c as lastc, 1 as len
from t
union all
select cte.type || t.type, t.type as lasttype, cte.len + 1
from cte join
t
on 1=1 and t.type > cte.lasttype
)
select type, count(*)
from (select name, cte.type, count(*)
from cte join
t
on cte.type like '%' || t.type || '%'
group by name, cte.type
having count(*) = length(cte.type)
) x
group by type
order by type;
There is no way to generate all possible combinations (A, B, C, AB, AC, BC, etc) in Amazon Redshift.
(Well, you could select each unique value, smoosh them into one string, send it to a User-Defined Function, extract the result into multiple rows and then join it against a big query, but that really isn't something you'd like to attempt.)
One approach would be to create a table containing all possible combinations — you'd need to write a little program to do that (eg using itertools in Python). Then, you could join the data against that reasonably easy to get the desired result (eg IF 'ABC' CONTAINS '%A%').

Column to rows, rows to rows in SQL Server

I have table like this
id | vname1 | vname2 | vname3
1 | vala | valb | valc
I want this to convert like this
id | vname | vals
1 | vname1 | vala
1 | vname2 | valb
1 | vname3 | valc
I thought about pivoting but here I think is not the case
Do a UNION ALL, with one SELECT for each vname column:
select id, 'vname1' as vname, vname1 as vals from tablename
union all
select id, 'vname2' as vname, vname2 as vals from tablename
union all
select id, 'vname3' as vname, vname3 as vals from tablename
You can use the UNPIVOT function to convert the columns into rows:
Sample Example:
select Id,
indicatorname,
from yourtable
unpivot
(
indicatorvalue
for indicatorname in (Indicator1, Indicator2, Indicator3)
) unpiv;
Link for reference: UnPivot

SQL 1 row twice

I have SQL table what looks like:
+----------+-----------+
| ID | Direction |
+----------+-----------+
| 1 | left |
| 1 | null |
| 2 | left |
| 2 | null |
| 3 | null |
| 4 | left |
| 4 | null |
| 5 | null |
+----------+-----------+
I want to show each value only once:
If there will be ID 1 with Direction null and left, then show only ID 1 with direction left.
If there will be ID 1 only with null value, show it with null value.
Use a common table expression (cte):
with cte as
(
Your huge select...
)
select *
from cte t1
where t1.Direction = 'left'
or not exists (select * from cte t2
where t2.kanbanid = t1.kanbanid
and t2.Direction = 'left')
I.e. if your select has Direction 'left' for a kanbanid, return that row. Also return that row if same kanbanid has no Direction 'left' at all.
Why wont below query work:
select id,max(dir)
from #temp
group by id
below is test data:
create table #temp
(
id int,
dir char(10)
)
insert into #temp
select 1,'left'
union all
select 1,null
union all
select 2,null
union all
select 3,'right'
union all
select 3,null
union all
select 3,null
select id,max(dir)
from #temp
group by id
aggregate functions will ignore null,below is the output:
select distinct *,
row_number() over (partition by id order by ,Direction )as row1 into #any_table
from #your_table_name
select * from #any_table
where row1 =1

Oracle complex string replacement

I've got the following table
mytable
type | id | name | formula
"simple" | 1 | "COUNT" | "<1>"
"simple" | 2 | "DISTINCT" | "<2>"
"simple" | 3 | "mycol" | "<3>"
"complex" | 4 | null | "<1>(<2> <3>)"
Now I would like to read this table and add an additional column which replaces the formula string.
For id 4 I would need: "COUNT(DISTINCT mycol)"
Any idea how I can do that?
In Oracle 11 it may looks like this:
select
type, id, name, formula, value
from
mytable
left join (
select
id_complex,
listagg(decode(pos, 2, name, part)) within group (order by occ, pos) as value
from
(
select
id_complex, occ, pos,
regexp_replace(pair, '^(.*?)(<.*?>)$', '\'||pos) as part
from
(
select
id as id_complex,
occ,
regexp_substr(formula||'<>', '.*?<.*?>', 1, occ) as pair
from
(
select level as occ from dual
connect by level <= (select max(length(formula)) from mytable)
),
mytable
where type = 'complex'
),
(select level as pos from dual connect by level <= 2)
)
left join mytable on part = formula and type = 'simple'
group by id_complex
) on id = id_complex
order by id
SQL Fiddle