基于 SQL (BigQuery) 中的多列返回许多小数据样本

Posted

技术标签:

【中文标题】基于 SQL (BigQuery) 中的多列返回许多小数据样本【英文标题】:Return many small data samples based on more than one column in SQL (BigQuery) 【发布时间】:2018-03-22 17:41:51 【问题描述】:

在这个例子中,我有一个图书数据库,每本书有一条记录。记录包含书的所有者、类型和其他一些信息。我需要返回每个所有者、每个流派的前 20 名样本。

我计划这样做的方式是以编程方式构建一个巨大的查询,例如:

(SELECT Owner, Genre, Data_one, Data_two FROM `dataset-table` WHERE Genre LIKE "%HORROR%" AND Owner LIKE "Alex" LIMIT 20)
UNION ALL 
(SELECT Owner, Genre, Data_one, Data_two FROM `dataset-table` WHERE Genre LIKE 
"%COMEDY%" AND Owner LIKE "Alex" LIMIT 20)
UNION ALL
(SELECT Owner, Genre, Data_one, Data_two FROM `dataset-table` WHERE Genre LIKE 
"%HORROR%" AND Name LIKE "Sarah" LIMIT 20)
UNION ALL
(SELECT Owner, Genre, Data_one, Data_two FROM `dataset-table` WHERE Genre LIKE 
"%COMEDY%" AND Owner LIKE "Sarah" LIMIT 20)
 UNION ALL
(SELECT Owner, Genre, Data_one, Data_two FROM `dataset-table` WHERE Genre LIKE 
"%HORROR%" AND Owner LIKE "James" LIMIT 20)
UNION ALL
(SELECT Owner, Genre, Data_one, Data_two FROM `dataset-table` WHERE Genre LIKE "%COMEDY%" AND Owner LIKE "James" LIMIT 20)

但是,我需要同时为几百个用户和 6 个流派执行此操作,我在 BigQuery 中收到一个错误,提示查询太复杂。

有没有人知道如何根据多个类别退回大量有限样本?如果它们最终出现在一个大数据集中,那很好。

例如:

    Owner       |       Genre       |       Data_one       |       Data_two
    Alex        |       Horror      |     Stephen King     |         IT
                  .... 100 more Horror books from Alex ....
    Sarah       |       Horror      |     Darren Shan      |      Lord Loss
                  .... 100 more Horror books from Sarah ....
    Alex        |       Comedy      |     Russel Brand     |        MBW
                  .... 100 more Comedy books from Alex ....

我会退回 Alex 的 20 部恐怖书、Sarah 的 20 部恐怖书和 Alex 的 20 部喜剧中的任何样本。

【问题讨论】:

请向我们提供所有者、名称、流派、data_one 和 data_two 的示例数据以及您的预期结果。 完成了,我实际上在原版中混淆了 Name 和 Owner,它们是同一个字段。 查看答案 - 它就是这样做的! 【参考方案1】:

以下是 BigQuery 标准 SQL 的示例

它使用bigquery-public-data.medicare.physicians_and_other_supplier_2012公共数据和姓名、城市、组织用于模仿您的作者、流派、数据

#standardSQL
WITH `project.dataset.table` AS (
  SELECT 
    nppes_provider_first_name name, 
    nppes_provider_city city, 
    nppes_provider_last_org_name org
  FROM `bigquery-public-data.medicare.physicians_and_other_supplier_2012`
), search AS (
  SELECT name, city FROM
  UNNEST(['JOHN','MICHAEL','DAVID','ROBERT']) name, 
  UNNEST(['NEW YORK','SPRINGFIELD','COLUMBUS','HOUSTON','DALLAS']) city
)
SELECT name, city, org FROM (
  SELECT name, city, ARRAY_AGG(DISTINCT org LIMIT 20) orgs
  FROM `project.dataset.table`
  JOIN search USING(name, city)
  WHERE RAND() < 0.5
  GROUP BY name, city
), UNNEST(orgs) org
ORDER BY name, city, org

所以对于您的情况 - 它可能如下所示

#standardSQL
WITH search AS (
  SELECT name, genre FROM
  UNNEST(['Alex','Sarah','James','Robert']) name, 
  UNNEST(['HORROR','COMEDY') genre
)
SELECT name, genre, org FROM (
  SELECT t.name, t.genre, ARRAY_AGG(DISTINCT 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

当然,JOINing 的逻辑仍然在你身上——相等或 LIKE 或 REGEXP 等

【讨论】:

非常感谢您的回答,我让它几乎可以完美地处理我的数据集。当你说加入的逻辑在我身上时,你的意思是每个“组织”运行一次并将数据连接在一起吗?我目前正试图在将“Data_two org2”添加到顶部选择后同时显示“Data_one”和“Data_two”,但被“UNNEST(orgs)org”挫败。为什么“orgs”是复数形式? 编辑:我明白为什么它是复数的,长度为 20 的数组是 orgs,但我尝试在设置中添加第二个变量并将其更改为:UNNEST(orgs) org, UNNEST(orgs2) org2 和我从 60 个结果跃升至 1200 个!我真的很感谢你在这里的知识。会继续努力解决的。 通过加入逻辑,我的意思是 ON 子句 (LOWER(s.name) = LOWER(t.name) AND LOWER(s.genre) = LOWER(t.genre)) 中的内容,正如我所提到的,这实际上取决于您要如何识别合格的流派和名称 - 通过平等、相似等。 如果您仍然对 data_one、data_two 有挑战 - 请发布新的具体问题,我们将很乐意回答。同时,请记住,这实际上取决于您实际表的架构。如果,例如,那些 data_one 和 dta_two 是组织记录的一部分 - 你可以在你的选择语句中使用org.data_one, org.data_two(当然使用实际的字段名称):o) 谢谢,我实际上对架构和 JOIN 很好。我在这里创建了一个新问题,希望能解释我的新问题:***.com/questions/49454231/…【参考方案2】:

我认为你可以使用row_number()

select t.*
from (select t.*,
             row_number() over (partition by name
                                order by (case when Genre like '%HORROR%' then 'HORROR'
                                               when Genre like '%COMEDY%' then 'COMEDY'
                                          end)
                               ) as seqnum
      from `dataset-table`
      where name in ('Alex', 'Sarah', 'James') and
            (genre like '%HORROR%' or genre like '%COMEDY%')
     ) t
where seqnum <= 20;

这不是 100% 等效的,因为任何归类为“恐怖喜剧”的电影也将被视为“恐怖”。另一方面,genre 不应包含多个值,除非它是重复记录或 JSON。在一个字符串中存储多个值是一种 SQL 反模式。

【讨论】:

以上是关于基于 SQL (BigQuery) 中的多列返回许多小数据样本的主要内容,如果未能解决你的问题,请参考以下文章

将多列转换为 Bigquery 中的记录

BigQuery 计算多列值之间的重叠百分比

Bigquery:将多列中的数据转换为行格式

基于 SQL Server 中的一列透视多列

需要将字符串从一列分隔为多列,以';'分隔bigquery中的分隔符

从多列返回值的 SQL 函数