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 / 245962
Accepted
Manngo
Manngo
Asked: 2019-08-22 21:58:57 +0800 CST2019-08-22 21:58:57 +0800 CST 2019-08-22 21:58:57 +0800 CST

Um ntile mais inteligente

  • 772

Ao usar a ntile()função de janela, o principal problema é que ela agrupa arbitrariamente em partes aproximadamente iguais, independentemente do valor real.

Por exemplo com a seguinte consulta:

select
    id,title,price,
    row_number() over(order by price) as row_number,
    rank() over(order by price) as rank,
    count(*) over(order by price) as count,
    dense_rank() over(order by price) as dense_rank,
    ntile(10) over(order by price) as decile
from paintings
order by price;

Obterei 10 grupos aproximadamente do mesmo tamanho, com a forte probabilidade de que pinturas com o mesmo preço acabem em caixas diferentes.

Por exemplo:

┌────┬────────────────────────────────────────────┬───────┬────────────┬──────┬───────┬────────────┬────────┐
│ id │ title                                      │ price │ row_number │ rank │ count │ dense_rank │ decile │
╞════╪════════════════════════════════════════════╪═══════╪════════════╪══════╪═══════╪════════════╪════════╡
│ 11 │ Eyes in the Heat                           │ 10    │ 1          │ 1    │ 1     │ 1          │ 1      │
│ 19 │ Deux fillettes, fond jaune et rouge        │ 11    │ 2          │ 2    │ 2     │ 2          │ 1      │
│ 17 │ Flowers in a Pitcher                       │ 12    │ 3          │ 3    │ 6     │ 3          │ 1      │
│ 5  │ Composition with Red, Yellow and Blue      │ 12    │ 4          │ 3    │ 6     │ 3          │ 2      │
│ 18 │ La lecon de musique (The Music Lesson)     │ 12    │ 5          │ 3    │ 6     │ 3          │ 2      │
│ 9  │ The Adoration of the Magi                  │ 12    │ 6          │ 3    │ 6     │ 3          │ 2      │
│ 29 │ Self-Portrait                              │ 14    │ 7          │ 7    │ 10    │ 4          │ 3      │
│ 25 │ Symphony in White, No. 1: The White Girl   │ 14    │ 8          │ 7    │ 10    │ 4          │ 3      │
│ 30 │ The Anatomy Lecture of Dr. Nicolaes Tulp   │ 14    │ 9          │ 7    │ 10    │ 4          │ 3      │
│ 20 │ Les repasseuses (Women Ironing)            │ 14    │ 10         │ 7    │ 10    │ 4          │ 4      │
│ 1  │ The Birth of Venus                         │ 15    │ 11         │ 11   │ 14    │ 5          │ 4      │
│ 12 │ Femme se promenant dans une foret exotique │ 15    │ 12         │ 11   │ 14    │ 5          │ 4      │
│ 24 │ Portrait of the Painter’s Mother           │ 15    │ 13         │ 11   │ 14    │ 5          │ 5      │
│ 28 │ Jeunes filles au piano                     │ 15    │ 14         │ 11   │ 14    │ 5          │ 5      │
│ 7  │ Portrait de l artiste (Self-portrait)      │ 16    │ 15         │ 15   │ 17    │ 6          │ 5      │
│ 3  │ The Last Supper                            │ 16    │ 16         │ 15   │ 17    │ 6          │ 6      │
│ 13 │ Combat of a Tiger and a Buffalo            │ 16    │ 17         │ 15   │ 17    │ 6          │ 6      │
│ 4  │ The Creation of Man                        │ 17    │ 18         │ 18   │ 19    │ 7          │ 6      │
│ 22 │ Le Chemin de Fer                           │ 17    │ 19         │ 18   │ 19    │ 7          │ 7      │
│ 6  │ Femmes de Tahiti [Sur la plage]            │ 18    │ 20         │ 20   │ 24    │ 8          │ 7      │
│ 21 │ Le Bar aux Folies-Berg                     │ 18    │ 21         │ 20   │ 24    │ 8          │ 7      │
│ 26 │ Lady at the Piano                          │ 18    │ 22         │ 20   │ 24    │ 8          │ 8      │
│ 15 │ Remembrance of a Garden                    │ 18    │ 23         │ 20   │ 24    │ 8          │ 8      │
│ 16 │ 1914                                       │ 18    │ 24         │ 20   │ 24    │ 8          │ 8      │
│ 14 │ Ancient Sound, Abstract on Black           │ 19    │ 25         │ 25   │ 28    │ 9          │ 9      │
│ 8  │ The Large Turf                             │ 19    │ 26         │ 25   │ 28    │ 9          │ 9      │
│ 23 │ On the Beach                               │ 19    │ 27         │ 25   │ 28    │ 9          │ 9      │
│ 2  │ Portrait of Mona Lisa                      │ 19    │ 28         │ 25   │ 28    │ 9          │ 10     │
│ 27 │ On the Terrace                             │ 20    │ 29         │ 29   │ 30    │ 10         │ 10     │
│ 10 │ The She-Wolf                               │ 20    │ 30         │ 29   │ 30    │ 10         │ 10     │
└────┴────────────────────────────────────────────┴───────┴────────────┴──────┴───────┴────────────┴────────┘

Observe que há quatro itens com preço 12, mas dois deles estão no decil 1 e dois deles no decil 2. Eu gostaria de manter esses itens juntos, e não estou preocupado com qual decil.

Incluí outras funções de janela para fazer a comparação.

Parece que ntile()usa o row_number()único e baseia os cortes nisso. Seria mais justo se ele usasse a função rank()ou count(*), pois itens com o mesmo preço acabariam na mesma caixa.

Esse é o comportamento do PostgreSQL e do SQL Server e, presumivelmente, do resto.

A questão é, existe uma maneira de conseguir isso?

sql-server postgresql
  • 3 3 respostas
  • 902 Views

3 respostas

  • Voted
  1. Best Answer
    Mikael Eriksson
    2019-08-22T23:04:52+08:002019-08-22T23:04:52+08:00

    Você pode usar rank()e fazer divisão inteira com número de linhas para cada bin.

    declare @T table(id int, title varchar(100), price int);
    
    insert into @T(id, title, price) values
    (19, 'Deux fillettes, fond jaune et rouge        ', 11),
    (17, 'Flowers in a Pitcher                       ', 12),
    (5 , 'Composition with Red, Yellow and Blue      ', 12),
    (18, 'La lecon de musique (The Music Lesson)     ', 12),
    (9 , 'The Adoration of the Magi                  ', 12),
    (29, 'Self-Portrait                              ', 14),
    (25, 'Symphony in White, No. 1: The White Girl   ', 14),
    (30, 'The Anatomy Lecture of Dr. Nicolaes Tulp   ', 14),
    (20, 'Les repasseuses (Women Ironing)            ', 14),
    (1 , 'The Birth of Venus                         ', 15),
    (12, 'Femme se promenant dans une foret exotique ', 15),
    (24, 'Portrait of the Painter’s Mother           ', 15),
    (28, 'Jeunes filles au piano                     ', 15),
    (7 , 'Portrait de l artiste (Self-portrait)      ', 16),
    (3 , 'The Last Supper                            ', 16),
    (13, 'Combat of a Tiger and a Buffalo            ', 16),
    (4 , 'The Creation of Man                        ', 17),
    (22, 'Le Chemin de Fer                           ', 17),
    (6 , 'Femmes de Tahiti [Sur la plage]            ', 18),
    (21, 'Le Bar aux Folies-Berg                     ', 18),
    (26, 'Lady at the Piano                          ', 18),
    (15, 'Remembrance of a Garden                    ', 18),
    (16, '1914                                       ', 18),
    (14, 'Ancient Sound, Abstract on Black           ', 19),
    (8 , 'The Large Turf                             ', 19),
    (23, 'On the Beach                               ', 19),
    (2 , 'Portrait of Mona Lisa                      ', 19),
    (27, 'On the Terrace                             ', 20),
    (10, 'The She-Wolf                               ', 20);
    
    declare @BinCount int = 10;
    declare @BinSize int;
    select @BinSize = 1 + count(*) / @BinCount from @T;
    
    select T.id,
           T.title,
           T.price,
           1 + rank() over(order by T.price) / @BinSize as decile
    from @T as T;
    

    Resultado:

    id  title                                       price  decile
    --- ------------------------------------------- ------ --------------------
    19  Deux fillettes, fond jaune et rouge         11     1
    17  Flowers in a Pitcher                        12     1
    5   Composition with Red, Yellow and Blue       12     1
    18  La lecon de musique (The Music Lesson)      12     1
    9   The Adoration of the Magi                   12     1
    29  Self-Portrait                               14     3
    25  Symphony in White, No. 1: The White Girl    14     3
    30  The Anatomy Lecture of Dr. Nicolaes Tulp    14     3
    20  Les repasseuses (Women Ironing)             14     3
    1   The Birth of Venus                          15     4
    12  Femme se promenant dans une foret exotique  15     4
    24  Portrait of the Painter’s Mother            15     4
    28  Jeunes filles au piano                      15     4
    7   Portrait de l artiste (Self-portrait)       16     5
    3   The Last Supper                             16     5
    13  Combat of a Tiger and a Buffalo             16     5
    4   The Creation of Man                         17     6
    22  Le Chemin de Fer                            17     6
    6   Femmes de Tahiti [Sur la plage]             18     7
    21  Le Bar aux Folies-Berg                      18     7
    26  Lady at the Piano                           18     7
    15  Remembrance of a Garden                     18     7
    16  1914                                        18     7
    14  Ancient Sound, Abstract on Black            19     9
    8   The Large Turf                              19     9
    23  On the Beach                                19     9
    2   Portrait of Mona Lisa                       19     9
    27  On the Terrace                              20     10
    10  The She-Wolf                                20     10
    

    e eu não estou preocupado com qual decil

    Observe que os compartimentos 2 e 8 com seus dados de amostra acabaram vazios.

    • 5
  2. McNets
    2019-08-23T00:10:46+08:002019-08-23T00:10:46+08:00

    Você pode simular a função WIDTH_BUCKET:

    CREATE FUNCTION dbo.width_bucket(@val decimal, @min_val decimal, @max_val decimal, @groups int)
    RETURNS int
    AS
    BEGIN
        DECLARE @res int;
    
        IF @val = @max_val RETURN @groups;
    
        RETURN CAST((@val - @min_val) / ((@max_val - @min_val) / @groups) AS int) + 1;
    END
    
    SELECT
        id, title, price, dbo.width_bucket(price, 11.0, 20.0, 10)
    FROM
        t;
    

    Ou sem função:

    DECLARE @groups int = 10;
    
    WITH mm AS
    (
        SELECT MIN(price) AS min_price, MAX(price) AS max_price
        FROM   t
    )
    SELECT
        id, title, price,
        CASE
            WHEN 
                price = max_price THEN @groups
            ELSE
                CAST((price - min_price) / ((max_price - min_price) / @groups) AS int) + 1
        END as ngroup
    FROM
        t
    CROSS APPLY (SELECT min_price, max_price FROM mm) mm;
    

    Observe que alterei o tipo de preço de int para decimal.

    identificação | título | preço | ngrupo
    -: | :-------------------------------------------- | :---- | -----:
    19 | Deux fillettes, fond jaune et rouge | 11h00 | 1
    17 | Flores em um jarro | 12h00 | 2
     5 | Composição com Vermelho, Amarelo e Azul | 12h00 | 2
    18 | La lecon de musique (A lição de música) | 12h00 | 2
     9 | A Adoração dos Magos | 12h00 | 2
    29 | Autorretrato | 14h00 | 4
    25 | Symphony in White, No. 1: The White Girl | 14h00 | 4
    30 | A Palestra de Anatomia do Dr. Nicolaes Tulp | 14h00 | 4
    20 | Les repasseuses (Women Ironing) | 14h00 | 4
     1 | O Nascimento de Vênus | 15h00 | 5
    12 | Femme se promenant dans une foret exotique | 15h00 | 5
    24 | Retrato da mãe do pintor | 15h00 | 5
    28 | Jeunes filles au piano | 15h00 | 5
     7 | Portrait de l artiste (Auto-retrato) | 16h00 | 6
     3 | A Última Ceia | 16h00 | 6
    13 | Combate de um tigre e um búfalo | 16h00 | 6
     4 | A Criação do Homem | 17h00 | 7
    22 | Le Chemin de Fer | 17h00 | 7
     6 | Femmes de Tahiti [Sur la plage] | 18h00 | 8
    21 | Le Bar aux Folies-Berg | 18h00 | 8
    26 | Senhora ao Piano | 18h00 | 8
    15 | Lembrança de um Jardim | 18h00 | 8
    16 | 1914 | 18h00 | 8
    14 | Som antigo, abstrato em preto | 19h00 | 9
     8 | A Grande Relva | 19h00 | 9
    23 | Na Praia | 19h00 | 9
     2 | Retrato de Mona Lisa | 19h00 | 9
    27 | No Terraço | 20h00 | 10
    10 | A Loba | 20h00 | 10
    

    db<>fique aqui

    • 1
  3. Manngo
    2019-08-23T22:55:59+08:002019-08-23T22:55:59+08:00

    Aceitei uma resposta, mas gostaria de ilustrar como coloquei a resposta em prática.

    WITH data AS (SELECT count(*)/10.0 AS bin FROM prints WHERE price<20)
    SELECT
        id,title,price,
        row_number() OVER(ORDER BY price) AS row_number,
        ntile(10) OVER(ORDER BY price) AS decile,
        floor((row_number() OVER(ORDER BY price)-1)/bin)+1 AS row_decile,
        floor((rank() OVER(ORDER BY price)-1)/bin)+1 AS rank_decile,
        floor((count(*) OVER(ORDER BY price)-1)/bin)+1 AS count_decile,
        bin
    FROM prints,data
    WHERE price<20
    ORDER BY price;
    

    Pontos de interesse:

    • Eu coloquei o tamanho do compartimento em um CTE em vez de em uma variável para tornar o resultado mais neutro de banco de dados
    • A WHEREcláusula é simplesmente forçar um tamanho de caixa estranho.
    • O 10.0é gerar um decimal em vez de um inteiro truncado.
    • O row_number()/bin+1cálculo é incluído para duplicar a ntilefunção nativa.

    Claro, existe o risco de perder decis quando há um número relativamente pequeno de linhas, mas pelo menos mantém os mesmos valores juntos.

    Agora é uma questão de decidir qual opção é mais do seu gosto.

    Violino: http://sqlfiddle.com/#!17/8bb42/1

    • 0

relate perguntas

  • Os procedimentos armazenados impedem a injeção de SQL?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Sequências Biológicas do UniProt no PostgreSQL

  • Como determinar se um Índice é necessário ou necessário

  • Qual é a diferença entre a replicação do PostgreSQL 9.0 e o Slony-I?

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