I can directly unnest an array from a literal in BQ as follows:
select * from unnest([1,2,3])
# [{"f0_": "1"},{"f0_": "2"},{"f0_": "3"}]
How can I do the same from a with statement? For example, something like:
with Table as (
select [1,2,3] as arr
) select * from unnest(Table.arr) ?? -- currently get unrecognized table name
What would be the correct syntax for the above? Here would be one example I came up with: is this the simplest way to do a general unnest?
with Movie as (
select "Spider-Man" as Title, ['Sci-Fi', 'Action'] as Genres
) select Movie.Title, Genre from Movie cross join unnest(Movie.Genres) Genre
If so, why can't the above be done without a cross join?
How can I do the same from a with statement?
with Table as (
select [1,2,3] as arr
)
select x from Table, unnest(arr) x
is this the simplest way to do a general unnest?
Yes!
Related
I have a table where the column data has a combination of values seperated by ';'. I would like to split them into rows for each column value.
Table data
Now I would like to split them into multiple rows for each value like
I have tried using the below SQL statement.
SELECT DISTINCT COL_NAME FROM "DB"."SCHEMA"."TABLE,
LATERAL FLATTEN(INPUT=>SPLIT(COL_NAME,';'))
But the output is not as expected. Attaching the query output below.
Basically the query does nothing to my data.
It could be achieved using SPLIT_TO_TABLE table function:
This table function splits a string (based on a specified delimiter) and flattens the results into rows.
SELECT *
FROM tab, LATERAL SPLIT_TO_TABLE(column_name, ';')
I was able to resolve this by using LATERAL FLATTERN like a joining table and selecting the value from it.
SELECT DISTINCT A.VALUE AS COL_NAME
FROM "DB"."SCHEMA"."TABLE",
LATERAL SPLIT_TO_TABLE(COL_NAME,';')A
Looks your data has multiple delimiters , We can leverage STRTOK_SPLIT_TO_TABLE function using multiple delimiters..
STRTOK_SPLIT_TO_TABLE
WITH data AS (
SELECT *
FROM VALUES
('Greensboro-High Point-Winston-Salem;Norfolk-Portsmouth-Newport News Washington, D.C. Roanoke-Lynchburg Richmond-Petersburg')
v( cities))
select *
from data, lateral strtok_split_to_table(cities, ';-')
order by seq, index;
Result:
Your first attempt was very close, you just need to access the out of the flatten, instead of the input to the flatten
so using this CTE for data:
WITH fake_data AS (
SELECT *
FROM VALUES
('Greensboro-High Point-Winston-Salem;Norfolk-Portsmouth-Newport News;Washington, D.C.;Roanoke-Lynchburg;Richmond-Petersburg'),
('Knoxville'),
('Knoxville;Memphis;Nashville')
v( COL_NAME)
)
if you had aliased you tables, and accessed the parts.
SELECT DISTINCT f.value::text as col_name
FROM fake_data d,
LATERAL FLATTEN(INPUT=>SPLIT(COL_NAME,';')) f
;
which is what you did in your provided answer, but via SPLIT_TO_TABLE
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(SPLIT_TO_TABLE(COL_NAME,';')) f
;
STRTOK_SPLIT_TO_TABLE also is the same thing:
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(strtok_split_to_table(COL_NAME,';')) f
;
Which can also be done via a strtok_to_array and FLATTEN that
SELECT DISTINCT f.value as col_name
FROM fake_data d,
TABLE(FLATTEN(input=>STRTOK_TO_ARRAY(COL_NAME,';'))) f
;
COL_NAME
Greensboro-High Point-Winston-Salem
Norfolk-Portsmouth-Newport News
Washington, D.C.
Roanoke-Lynchburg
Richmond-Petersburg
Knoxville
Memphis
Nashville
simple search query
select * from table where id = id_1;
id_1,......................................id_2000
is there a way to search all these ids with one query or pl/sql code?
You could try:
SELECT * FROM table WHERE id IN (id_1, id_2, ... id_2000);
or if the IDs are strings:
SELECT * FROM table WHERE id IN ('id_'1, 'id_2', ... 'id_2000');
I am writing a HIVE query to pull about 2,000 unique keys from a table.
I keep getting this error - java.lang.StackOverflowError
My query is basic but looks like this:
SELECT * FROM table WHERE (Id = 1 or Id = 2 or Id = 3 Id = 4)
my WHERE clause goes all the way up to 2000 unique id's and I receive the error above. Does anyone know of a more efficient way to do this or get this query to work?
Thanks!
You may use the SPLIT and EXPLODE to convert the comma separated string to rows and then use IN or EXISTS.
using IN
SELECT * FROM yourtable t WHERE
t.ID IN
(
SELECT
explode(split('1,2,3,4,5,6,1998,1999,2000',',')) as id
) ;
Using EXISTS
SELECT * FROM yourtable t WHERE
EXISTS
(
SELECT 1 FROM (
SELECT
explode(split('1,2,3,4,5,6,1998,1999,2000',',')) as id
) s
WHERE s.id = t.id
);
Make use of the Between clause instead of specifying all unique ids:
SELECT ID FROM table WHERE ID BETWEEN 1 AND 2000 GROUP BY ID;
i you can create a table for these IDs and after use the condition of exist in the new table to get only your specific IDs
I would like to transform a pivot table into a flat table, but in the following fashion: consider the simple example of this table:
As you can see, for each item - Address or Income -, we have a column for old values, and a column for new (updated values). I would like to convert the table to a "flat" table, looking like:
Is there an easy way of doing that?
Thank you for your help!
In order to get the result, you will need to UNPIVOT the data. When you unpivot you convert the multiple columns into multiple rows, in doing so the datatypes of the data must be the same.
I would use CROSS APPLY to unpivot the columns in pairs:
select t.employee_id,
t.employee_name,
c.data,
c.old,
c.new
from yourtable t
cross apply
(
values
('Address', Address_Old, Address_new),
('Income', cast(income_old as varchar(15)), cast(income_new as varchar(15)))
) c (data, old, new);
See SQL Fiddle with demo. As you can see this uses a cast on the income columns because I am guessing it is a different datatype from the address. Since the final result will have these values in the same column the data must be of the same type.
This can also be written using CROSS APPLY with UNION ALL:
select t.employee_id,
t.employee_name,
c.data,
c.old,
c.new
from yourtable t
cross apply
(
select 'Address', Address_Old, Address_new union all
select 'Income', cast(income_old as varchar(15)), cast(income_new as varchar(15))
) c (data, old, new)
See Demo
select employee_id,employee_name,data,old,new
from (
select employee_id,employee_name,adress_old as old,adress_new as new,'ADRESS' as data
from employe
union
select employee_id,employee_name,income_old,income_new,'INCOME'
from employe
) data
order by employee_id,data
see this fiddle demo : http://sqlfiddle.com/#!2/64344/7/0
For debugging purpose I want to create pseudo "result set" in order to join them, like:
with tmp_tbl as ( select v from dual where v in ('cat', 'dog', 'fish') )
select read_tbl.* from tmp_tbl
left outer join read_tbl on real_tbl.id = tmp_tbl.id;
I understand that above expression is invalid and can be transformed into another which works. But my real example too complicate to shown here.
My question how to make this expression:
select v from dual where v in ('cat', 'dog', 'fish')
a valid result set so I can use it with joins and from keywords?
dual doesn't have v column. I look for a way to break SQL syntax to avoid create table calls..
I'm still not quite sure what you're trying to do, but it looks to me like you want a dummy table with fixed values. If so you can select multiple dummy values from dual and union all the results, which will give you multiple rows. You can then use that as a sub-select, or if you're effectively masking a real table (from the 'debug' comment) then a CTE might be clearer:
with tmp_tbl as (
select 'cat' as id from dual
union all select 'dog' from dual
union all select 'fish' from dual
)
select tmp_tbl.id, read_tbl.*
from tmp_tbl
left outer join real_tbl
on real_tbl.id = tmp_tbl.id;
You referred to a v column in the text, but you're joining on id, so I've aliased the fixed value as id inside the CTE (it only needs to be named in the first row). You can just change that to something else if you prefer. And you can of course select several fixed values (with different aliases) in each select from dual to make it look more like a real table.
For this purpose you can use subquery factoring, also known as “the with clause”
with t as
( select v from dial where v in ('cat','dog','fish') )
Select * from t
Oracle may decide to materialize this result set internally or not. If you want to control this behavior, you can use the optimizer hints “materialize” and “inline”.
Hope this helps.
Regards,
Rob.
Just enclose the query in brackets and give it a name, than you can use it in joins as you wish:
SELECT *
FROM ( select v from dial where v in ('cat', 'dog', 'fish') ) tmp_table
JOIN other_table ON tmp_table.v = other_table.v
WHERE tmp_table.v = xxx etc