lateral view explode in bigquery - sql

I want to do something like this using BigQuery.
Input Table
Col1
Col2
Col3
Col4
1
A,B,C
123
789
Output Table
ID
COL
VALUE
1
COL1
1
1
COL2
A,B,C
1
COL3
123
1
COL4
789
I got this in hive with LATERAL VIEW explode(MAP), but I can't get the same in bigquery.

Consider below approach
select id, col, value
from (select *, row_number() over() as id from your_table)
unpivot (value for col in (Col1, Col2, Col3, Col4))
f apply to sample data in your question
with your_table as (
select '1' Col1, 'A,B,C' Col2, '123' Col3, '789' Col4
)
output is
Note - this particular approach requires all columns (Col1 - Col4) to be of the same type. If this is not a case you will need first apply cast for some of those to make them string

If it's a discrete number of columns, you can use UNIONs for this...
select id, 'Col1' as Column, col1 as Value
from table
union all
select id, 'Col2' as Column, col2 as Value
from table
union all
select id, 'Col3' as Column, col3 as Value
from table

Related

How to use collec_set in hive to generate 0 for null

We have requirement to collect the values for all the different transection and display in "|" delimited and display 0 for the not available merchant.
table_1
col1
col2
col3
129867
paytm
4
18945
paytm
5
129867
payzap
6
18945
payzap
4
456312
paytm
3
we need to read the table1 and transform it into tabl2 as given below:
table_2
col1
col2
129857
4l6
18945
5l4
456312
3l0
suppose we have two merchant i.e paytm and payzap, how to achieve this in hive.
I have tried like:
SELECT col1,
Nvl(Concat_ws('|', Collect_set(col3)), 0) AS col2
FROM table_1
GROUP BY col1;
but I am not getting desired result.
If you using sql server then use string_agg
SELECT col1,
CASE
WHEN col2 NOT LIKE '%|%' THEN Concat(col2, '|0')
ELSE col2
END AS col2
FROM (SELECT col1,
String_agg(col3, '|') col2
FROM table
GROUP BY col1) B
In Hivesql I have provided you with syntax
SELECT col1,
CASE
WHEN col2 NOT LIKE '%|%' THEN Concat_ws(col2, '|0')
ELSE col2
END AS col2
FROM (SELECT col1,
Concat_ws('|', Collect_set(col3)) AS col2
FROM table_1
GROUP BY col1) A

How to get min value from multiple columns for a row in SQL

I need to get to first (min) date from a set of 4 (or more) columns.
I tried
select min (col1, col2, col3) from tbl
which is obviouslly wrong.
let's say I have these 4 columns
col1 | col2 | col3 | col4
1/1/17 | 2/2/17 | | 3/3/17
... in this case what I want to get is the value in col1 (1/1/17). and Yes, these columns can include NULLs.
I am running this in dashDB
the columns are Date data type,
there is no ID nor Primary key column in this table,
and I need to do this for ALL rows in my query,
the columns are NOT in order. meaning that col1 does NOT have to be before col2 or it has to be null AND col2 does NOT have to be before col3 or it has to be NULL .. and so on
If your DB support least function, it is the best approach
select
least
(
nvl(col1,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col2,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col3,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col4,TO_DATE('2901-01-01','YYYY-MM-DD'))
)
from tbl
Edit: If all col(s) are null, then you can hardcode the output as null. The below query should work. I couldn't test it but this should work.
select
case when
least
(
nvl(col1,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col2,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col3,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col4,TO_DATE('2901-01-01','YYYY-MM-DD'))
)
= TO_DATE('2901-01-01','YYYY-MM-DD')
then null
else
least
(
nvl(col1,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col2,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col3,TO_DATE('2901-01-01','YYYY-MM-DD')),
nvl(col4,TO_DATE('2901-01-01','YYYY-MM-DD'))
)
end
as min_date
from tbl
If a id column in your table. Then
Query
select t.id, min(t.col) as min_col_value from(
select id, col1 as col from your_table
union all
select id, col2 as col from your_table
union all
select id, col3 as col from your_table
union all
select id, col4 as col from your_table
)t
group by t.id;
If you want the first date, then use coalesce():
select coalesce(col1, col2, col3, col4)
from t;
This returns the first non-NULL value (which is one way that I interpret the question). This will be the minimum date, if the dates are in order.
Select Id, CaseWhen (Col1 <= Col2 OR Col2 is null) And (Col1 <= Col3 OR Col3 is null) Then Col1 When (Col2 <= Col1 OR Col1 is null) And (Col2 <= Col3 OR Col3 is null) Then Col2 Else Col3 End As Min From YourTable
This is for 3 Column, Same way you can write for 4 - or more column.

Oracle SQL - Join 2 table columns in 1 row

I have 2 SQL's and the result come fine. They are no relation between those 2 queries but I want to see all the rows in single column.
e.g.
Select col1,col2,sum(col3) as col3 from table a
select col4,col5 from table b
I would like the result to be
col1 col2 col3 col4 col5
If there is no equivalent row for either table a or table b replace with zeroes.
Could some one help me with this. thanks.
Since, you didn't provided any information like table structure or data inside each tables. You can cross join both tables.
select t.col1,t.col2,t.col3,t1.col1,t1.col2 from tab1 t,tab2 t1;
SQLFiddle
In both select statements add column based on rownum or row_number() and then full join results using this column:
select nvl(col1, 0) col1, nvl(col2, 0) col2, nvl(col3, 0) col3,
nvl(col4, 0) col4, nvl(col5, 0) col5
from
(select rownum rn, col1, col2, col3 from (
select col1, col2, sum(col3) col3 from tableA group by col1, col2)) a
full join (select rownum rn, col4, col5 from tableB) b using (rn)
SQLFiddle demo
I guess a UNION could be a pragmatic solution since the 2 queries are not related. They are just 2 data sets that should be retrieved in one statement:
Select col1,col2,sum(col3) as col3 from table a
UNION
select col4,col5, to_number(null) col6 from table b
Be aware of col6 in the example. SQL insists on retrieving an equal set of columns in a UNION statement. It is a good practice to retrieve columns with exactly the same datatype. Since the sum(col3) will yield a number datatype column, col6 should too.
The outcome of col4 and col5 will be shown in col1 and col2.

How do you copy a row in a SQL table and alter one of the column values?

The answers for this question almost answer mine, but not quite. How do I turn this:
col0 col1 col2
data0 a foo
data1 b foo
data2 c fee
data3 d fee
into this? (duplicating the foo rows only)
col0 col1 col2
data0 a foo
data1 b foo
data2 c fee
data3 d fee
data0 a bar
data1 b bar
Where bar is from the statement, not the data, and the original table has 2 new rows.
insert into T (col0, col1, col2)
select col0, col1, 'bar'
from T
If by "copy" you mean a select then a union would work as in other answers or you could try this:
select col0, col1, case when num = 0 then col2 else 'bar' end as col2
from T, (select 0 as num union all select 1) as dup
One option, union all:
select col0, col1, col2
from yourtable
union all
select col0, col1, 'bar'
from yourtable
Assuming you just wanted the results for these two hard-coded strings, the following query would provide that for you.
SELECT
col0,
col1,
'foo'
FROM MyTable
UNION ALL
SELECT
col0,
col1,
'bar'
FROM MyTable;
A more practical scenario is to use a temp table so you're not duplicating your query for each scenario.
CREATE TABLE #Options
(
col2 VARCHAR(50)
);
INSERT INTO #Options VALUES ('foo'), ('bar');
SELECT
col0,
col1,
#Options.col2
FROM MyTable
CROSS JOIN #Options;
select col0, col1, col2
from yourtable
union all
select col0, col1, 'bar'
from yourtable
where col2 = 'foo'

select all columns with one column has different value

In my table,some records have all column values are the same, except one. I need write a query to get those records. what's the best way to do it? the table is like this:
colA colB colC
a b c
a b d
a b e
What's the best way to get all records with all the columns? Thanks for everyone's help.
Assuming you know that column3 will always be different, to get the rows that have more than one value:
SELECT Col1, Col2
FROM Table t
GROUP BY Col1, Col2
HAVING COUNT(distinct col3) > 1
If you need all the values in the three columns, then you can join this back to the original table:
SELECT t.*
FROM table t join
(SELECT Col1, Col2
FROM Table t
GROUP BY Col1, Col2
HAVING COUNT(distinct col3) > 1
) cols
on t.col1 = cols.col1 and t.col2 = cols.col2
Just select those rows that have the different values:
SELECT col1, col2
FROM myTable
WHERE colWanted != knownValue
If this is not what you are looking for, please post examples of the data in the table and the wanted output.
How about something like
SELECT Col1, Col2
FROM Table
GROUP BY Col1, Col2
HAVING COUNT(*) = 1
This will give you Col1, Col2 that have unique data.
Assuming col3 has the difs
SELECT Col1, Col2
FROM Table
GROUP BY Col1, Col2
HAVING COUNT(*) > 1
OR TO SHOW ALL 3 COLS
SELECT Col1, Col2, Col3
FROM Table1
GROUP BY Col1, Col2, Col3
HAVING COUNT(Col3) > 1