AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 161237
Accepted
vulkoingim
vulkoingim
Asked: 2017-01-17 07:51:47 +0800 CST2017-01-17 07:51:47 +0800 CST 2017-01-17 07:51:47 +0800 CST

Como obter a linha mais recente por tipo e realizar cálculos, dependendo do tipo de linha?

  • 772

Postei a mesma pergunta no SO, mas pensei em tentar aqui também, já que qualquer outro tipo de otimização e conselho são muito bem-vindos :) De qualquer forma, aqui está minha postagem, palavra por palavra:

Preciso de ajuda para escrever/otimizar uma consulta para recuperar a versão mais recente de cada linha por tipo e realizar alguns cálculos dependendo do tipo. Acho que seria melhor se eu ilustrasse com um exemplo.

Dado o seguinte conjunto de dados:

+-------+-------------------+---------------------+-------------+---------------------+--------+----------+
| id    | event_type        | event_timestamp     | message_id  | sent_at             | status | rate     |
+-------+-------------------+---------------------+-------------+---------------------+--------+----------+
| 1     | create            | 2016-11-25 09:17:48 | 1           | 2016-11-25 09:17:48 | 0      | 0.500000 |
| 2     | status_update     | 2016-11-25 09:24:38 | 1           | 2016-11-25 09:28:49 | 1      | 0.500000 |
| 3     | create            | 2016-11-25 09:47:48 | 2           | 2016-11-25 09:47:48 | 0      | 0.500000 |
| 4     | status_update     | 2016-11-25 09:54:38 | 2           | 2016-11-25 09:48:49 | 1      | 0.500000 |
| 5     | rate_update       | 2016-11-25 09:55:07 | 2           | 2016-11-25 09:50:07 | 0      | 1.000000 |
| 6     | create            | 2016-11-26 09:17:48 | 3           | 2016-11-26 09:17:48 | 0      | 0.500000 |
| 7     | create            | 2016-11-27 09:17:48 | 4           | 2016-11-27 09:17:48 | 0      | 0.500000 |
| 8     | rate_update       | 2016-11-27 09:55:07 | 4           | 2016-11-27 09:50:07 | 0      | 2.000000 |
| 9     | rate_update       | 2016-11-27 09:55:07 | 2           | 2016-11-25 09:55:07 | 0      | 2.000000 |
+-------+-------------------+---------------------+-------------+---------------------+--------+----------+

O resultado esperado deve ser:

+------------+--------------------+--------------------+-----------------------+
| sent_at    | sum(submitted_msg) | sum(delivered_msg) | sum(rate_total)       |
+------------+--------------------+--------------------+-----------------------+
| 2016-11-25 |                  2 |                  2 |              2.500000 |
| 2016-11-26 |                  1 |                  0 |              0.500000 |
| 2016-11-27 |                  1 |                  0 |              2.000000 |
+------------+--------------------+--------------------+-----------------------+

No final do post está a consulta que é utilizada para obter este resultado. Estou disposto a apostar que deve haver uma maneira de otimizá-lo, já que está usando subconsultas com junções e, pelo que li sobre o BigQuery, é melhor evitar junções. Mas primeiro alguns antecedentes:

Em essência, o conjunto de dados representa uma tabela anexada, na qual vários eventos são gravados. O tamanho dos dados está na casa das centenas de milhões e crescerá para mais de bilhões. Como as atualizações no BigQuery não são práticas e os dados estão sendo transmitidos para o BQ, preciso de uma maneira de recuperar o mais recente de cada evento, realizar alguns cálculos com base em determinadas condições e retornar um resultado preciso. A consulta é gerada dinamicamente, com base na entrada do usuário, portanto, mais campos/cálculos podem ser incluídos, mas foram omitidos para simplificar.

  • Há apenas um createevento, mas nde qualquer outro tipo
  • Para cada grupo de eventos, apenas o último deve ser levado em consideração ao fazer os cálculos.
    • status_update - atualiza o status
    • rate_update - atualiza a taxa
    • criar - auto explicativo
  • Todo evento que não é createpode não carregar o restante das informações do original/pode não ser preciso (exceto para message_id e o campo em que o evento está operando) (o conjunto de dados é simplificado, mas imagine que há muito mais colunas e mais eventos serão adicionados mais tarde)
    • Por exemplo, a rate_updatepode ou não ter o campo de status definido, ou ser um valor que não é o final, então nenhum cálculo pode ser feito no campo de status de um rate_updateevento e o mesmo vale parastatus_update
  • Pode-se supor que a tabela seja particionada por data e cada consulta fará uso das partições. Essas condições foram omitidas em favor da simplicidade por enquanto.

Então acho que tenho algumas perguntas:

  • Como essa consulta pode ser otimizada?
  • Será uma ideia melhor colocar os eventos, além de createsuas próprias tabelas, onde os únicos campos disponíveis serão os relevantes para os eventos e necessários para as junções (message_id, event_timestamp)? Isso reduzirá a quantidade de dados processados?
  • Qual seria a maneira ideal de adicionar mais eventos no futuro, que terão suas próprias condições e cálculos?

Na verdade, qualquer conselho sobre como consultar esse conjunto de dados de maneira eficiente e amigável é mais do que bem-vindo! Obrigada! :)

A monstruosidade que eu inventei é a seguinte. São INNER JOINSusados ​​para recuperar a versão mais recente de cada linha, conforme este recurso

    select
    sent_at as sent_at,
    sum(submitted_msg) as submitted,
    sum(delivered_msg) as delivered,
    sum(sales_rate_total) as sales_rate_total
    FROM (

      #DELIVERED
        SELECT 
            d.message_id,
            FORMAT_TIMESTAMP('%Y-%m-%d 00:00:00', sent_at) AS sent_at,
            0 as submitted_msg,
            sum(if(status=1,1,0)) as delivered_msg,
            0 as sales_rate_total
        FROM `events` d
        INNER JOIN
                (
                    select message_id, max(event_timestamp) as ts 
                    from `events` 
                    where event_type = "status_update" 
                    group by 1
                    ) g on d.message_id = g.message_id and d.event_timestamp = g.ts
        GROUP BY 1,2

        UNION ALL

      #SALES RATE
        SELECT 
            s.message_id,
            FORMAT_TIMESTAMP('%Y-%m-%d 00:00:00', sent_at) AS sent_at,
            0 as submitted_msg,
            0 as delivered_msg,
            sum(sales_rate) as sales_rate_total
        FROM `events` s
        INNER JOIN 
                    (
                    select message_id, max(event_timestamp) as ts 
                    from `events` 
                    where event_type in ("rate_update", "create")  
                    group by 1
                    ) f on s.message_id = f.message_id and s.event_timestamp = f.ts
        GROUP BY 1,2

        UNION ALL

      #SUBMITTED & REST
        SELECT 
            r.message_id,
            FORMAT_TIMESTAMP('%Y-%m-%d 00:00:00', sent_at) AS sent_at,
            sum(if(status=0,1,0)) as submitted_msg,
            0 as delivered_msg,
            0 as sales_rate_total
        FROM `events` r
        INNER JOIN
                (
                    select message_id, max(event_timestamp) as ts 
                    from `events` 
                    where event_type = "create" 
                    group by 1
                    ) e on r.message_id = e.message_id and r.event_timestamp = e.ts
        GROUP BY 1, 2

    ) k
    group by 1
optimization google-bigquery
  • 1 1 respostas
  • 52 Views

1 respostas

  • Voted
  1. Best Answer
    McNets
    2017-01-17T14:59:07+08:002017-01-17T14:59:07+08:00

    1: Usei o SQL-SERVER no rextester para estudar seus dados, mas acho que pode ser aplicado ao google-bigquery.
    2. Nunca trabalhei com google-bigquery.
    3: Inglês não é minha primeira língua.
    4. Posso tomar uma aspirina?

    Primeiro, acho que há algo errado em sua tabela de resultados. Usando sua subconsulta para obter a taxa de vendas:

    Confira aqui: http://rextester.com/CHX54701

    select e.*
    from events e
        inner join (
                    select message_id, max(event_timestamp) as event_timestamp 
                    from events 
                    where event_type in ('rate_update', 'create')  
                    group by message_id        
                   ) t 
                   on t.message_id = e.message_id and t.event_timestamp = e.event_timestamp;
    
    +----+-------------+---------------------+------------+---------------------+--------+------+
    | id |  event_type |   event_timestamp   | message_id |       sent_at       | status | rate |
    +----+-------------+---------------------+------------+---------------------+--------+------+
    |  1 |    create   | 25.11.2016 09:17:48 |      1     | 25.11.2016 09:17:48 |    0   |  0,5 |
    +----+-------------+---------------------+------------+---------------------+--------+------+
    |  9 | rate_update | 27.11.2016 09:55:07 |      2     | 25.11.2016 09:55:07 |    0   |   2  |
    +----+-------------+---------------------+------------+---------------------+--------+------+
    |  6 |    create   | 26.11.2016 09:17:48 |      3     | 26.11.2016 09:17:48 |    0   |  0,5 |
    +----+-------------+---------------------+------------+---------------------+--------+------+
    |  8 | rate_update | 27.11.2016 09:55:07 |      4     | 27.11.2016 09:50:07 |    0   |   2  |
    +----+-------------+---------------------+------------+---------------------+--------+------+
    

    sum(rate) para 2016-11-25 deve ser 3,0 em vez de 2,5

    Isso está correto?, porque se nem tudo acaba aqui.

    Na minha humilde opinião, você está agrupando em cada subconsulta todos os seus registros message_ide obtendo max(event_timestamp)Então, dependendo de event_typeobter a soma () de valores diferentes.

    Então, minha primeira tentativa foi obter todos os max(event_timestamp) agrupados por message_id:

    select message_id, event_type, max(event_timestamp) event_timestamp
    from events
    group by message_id, event_type;
    
    +------------+---------------+---------------------+
    | message_id |   event_type  |   event_timestamp   |
    +------------+---------------+---------------------+
    |      1     |     create    | 25.11.2016 09:17:48 |
    |      2     |     create    | 25.11.2016 09:47:48 |
    |      3     |     create    | 26.11.2016 09:17:48 |
    |      4     |     create    | 27.11.2016 09:17:48 |
    |      2     |  rate_update  | 27.11.2016 09:55:07 |
    |      4     |  rate_update  | 27.11.2016 09:55:07 |
    |      1     | status_update | 25.11.2016 09:24:38 |
    |      2     | status_update | 25.11.2016 09:54:38 |
    +------------+---------------+---------------------+
    

    Então, em vez de usar 3 UNIONS de 3 consultas + subconsultas, acho que pode ser feito com um único JOIN assim:

    with ct as
    (
      select message_id, event_type, max(event_timestamp) event_timestamp, convert(varchar(20),max(sent_at),112) st
      from events
      group by message_id, event_type
    )
    select 
           max(e.sent_at) sent_at, 
           sum(case when e.event_type='create' and status=0 then 1 else 0 end) as submitted_msg,
           sum(case when e.event_type='status_update' and status=1 then 1 else 0 end) as delivered_msg,
           sum(case when (e.event_type='create' or e.event_type='rate_update') and status=0 then rate else 0 end) as sum_rate  
    from events e
         inner join ct on ct.message_id = e.message_id and ct.event_timestamp = e.event_timestamp
    group by st
    order by sent_at
    

    Pesquisei informações sobre o googe-bigquery e ele permite usar sentenças CTE, mas você pode reescrevê-lo como um JOIN (SUBQUERY....

    Como você pode ver, usei 3 CASE diferentes para SUM valores desejados.

    O resultado final é:

    +---------------------+---------------+---------------+----------+
    |       sent_at       | submitted_msg | delivered_msg | sum_rate |
    +---------------------+---------------+---------------+----------+
    | 25.11.2016 09:55:07 |       2       |       2       |     3    |
    +---------------------+---------------+---------------+----------+
    | 26.11.2016 09:17:48 |       1       |       0       |    0,5   |
    +---------------------+---------------+---------------+----------+
    | 27.11.2016 09:50:07 |       1       |       0       |    2,5   |
    +---------------------+---------------+---------------+----------+
    

    Confira o resultado final aqui: http://rextester.com/FDIWA74637

    • 0

relate perguntas

  • Como o Yelp calcula com eficiência a distância no banco de dados?

  • Otimização de consultas

  • Como devo otimizar o armazenamento para esta tabela?

  • DBMS_REDEFINITION vs EXCHANGE PARTITION no oracle

  • Existe uma boa "regra de ouro" para traduzir o custo EXPLAIN para o tempo de execução (relógio de parede)?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve