In this example, I have a book database, with one record per book. The records contain the book owners, the genre, and some other info. I need to return a sample of the top 20 per owner, per genre, along with all the data in the row.
I have this code, which does what I need for one data point in the row (Data_one):
WITH `project.dataset.table` AS (
SELECT
Name name,
Genre genre,
Data_one org
FROM `project.dataset.booktable`
), search AS (
SELECT name, genre FROM
UNNEST(['Alex','James']) name,
UNNEST(['HORROR','COMEDY']) genre
)
SELECT name, genre, org
FROM (
SELECT t.name, t.genre, ARRAY_AGG(t.org LIMIT 20) orgs
FROM `project.dataset.table` t JOIN search s
ON LOWER(s.name) = LOWER(t.name)
AND LOWER(s.genre) = LOWER(t.genre)
WHERE RAND() < 0.5
GROUP BY t.name, t.genre
), UNNEST(orgs) org
ORDER BY name, genre, org
But when I try to extend it to work for a second (and eventually quite a few) piece of data from the row, it inflates the records returned by a factor of 200:
WITH `project.dataset.table` AS (
SELECT
Name name,
Genre genre,
Data_one org,
Data_two org2
FROM `project.dataset.booktable`
), search AS (
SELECT name, genre FROM
UNNEST(['Alex','James']) name,
UNNEST(['HORROR','COMEDY']) genre
)
SELECT name, genre, org, org2
FROM (
SELECT t.name, t.genre, ARRAY_AGG(t.org LIMIT 20) orgs, ARRAY_AGG(t.org2 LIMIT 20) orgs2
FROM `project.dataset.table` t JOIN search s
ON LOWER(s.name) = LOWER(t.name)
AND LOWER(s.genre) = LOWER(t.genre)
WHERE RAND() < 0.5
GROUP BY t.name, t.genre
), UNNEST(orgs) org, UNNEST(orgs2) org2
ORDER BY name, genre, org, org2
I know UNNEST turns an array into a table, but is this somehow creating an array of an array and unnesting that? I am unfamiliar with the syntax.
Edit: The data I am trying to get is all on the same level, all single data points (no arrays) and a mixture of NULLABLE STRINGS, INTEGERS, TIMESTAMPS, FLOATS
E.G:
Genre STRING NULLABLE
Name STRING NULLABLE
Data_one STRING NULLABLE
Data_two STRING NULLABLE
Data_three INTEGER NULLABLE
Data_four TIMESTAMP NULLABLE
Owner | Genre | Data_one | Data_two |Data_three|Data_four
Alex | Horror | Stephen King | IT | 3 |2018-01-02
Alex | Sci-fi | Andy Weir |The Martian | 5 |2018-01-02
James | Horror | Bram Stoker | Dracula | 2 |2018-01-02
Sarah | Horror | Stephen King | The Stand | 3 |2018-01-02
James | Horror | Stephen King |Pet Sematary| 2 |2018-01-02
as your question leaks details - below answer is just a direction for you to explore
#standardSQL
SELECT name, genre, data_one, data_two FROM (
SELECT t.name, t.genre, ARRAY_AGG(t.org LIMIT 20) orgs, ARRAY_AGG(t.org2 LIMIT 20) orgs2
FROM `project.dataset.table` t JOIN search s
ON LOWER(s.name) = LOWER(t.name)
AND LOWER(s.genre) = LOWER(t.genre)
WHERE RAND() < 0.5
GROUP BY t.name, t.genre
), UNNEST(orgs) data_one WITH OFFSET pos1
, UNNEST(orgs2) data_two WITH OFFSET pos2
WHERE pos1 = pos2
ORDER BY name, genre, data_one
As you can see - here OFFSET was introduced identifying position of elements within the array and then leaving in result only those combinations which have same positions
In real use case - you most likely have some yet another field that identifies which data_one and data_two belong to the same row and that field can be used to pair those data_one and data_two
Hope this helped to get you direction
Update
as you added schema/example - see below
#standardSQL
SELECT name, genre, data.data_one, data.data_two, data.data_three, data.data_four
FROM (
SELECT t.name, t.genre,
ARRAY_AGG(STRUCT(data_one, data_two, data_three, data_four) LIMIT 20) data
FROM `project.dataset.table` t JOIN search s
ON LOWER(s.name) = LOWER(t.name)
AND LOWER(s.genre) = LOWER(t.genre)
WHERE RAND() < 0.5
GROUP BY t.name, t.genre
), UNNEST(data) data
ORDER BY name, genre
That is exactly what I mentioned in comments to your very first related question in another post (you can just use org.data_one, org.data_two in you select statement)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With