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 / 59399
Accepted
Snowball
Snowball
Asked: 2014-02-22 22:05:04 +0800 CST2014-02-22 22:05:04 +0800 CST 2014-02-22 22:05:04 +0800 CST

Retângulos não sobrepostos restritos a um limite

  • 772

Estou tentando modelar a colocação de peças em uma placa de circuito. Sem nenhuma restrição significativa, meu esquema básico se parece com isso:

create table part (
    part_id bigserial primary key,
    name text not null,
    width double precision not null,
    height double precision not null
);
create table board (
    board_id bigserial primary key,
    width double precision not null,
    height double precision not null
);
create table board_part (
    board_id bigint not null references board,
    part_id bigint not null references part,
    position point not null
);

( SQL Fiddle , Visualização )

Para be b2qualquer board_parts, desejo impor as seguintes restrições:

  1. bencontra-se no tabuleiro:

    box(b.position, point(b.part.width,b.part.height))
        <@ box(point(0,0), point(b.board.width,b.board.height))
    
  2. be b2não se sobreponham se estiverem no mesmo tabuleiro:

    b.board_id != b2.board_id or
    not (box(b.position, point(b.part.width,b.part.height))
            && box(b2.position, point(b2.part.width,b2.part.height)))
    

Como posso conseguir isso (sem muita duplicação de dados)? Mudar o esquema é bom.

Aqui está minha melhor tentativa (SQL Fiddle) , inspirando-se na resposta de Erwin à minha pergunta anterior . Ele impõe as restrições que eu queria, mas tem muitos dados duplicados na board_parttabela. Imagino que poderia escrever uma função para preencher os campos board_width, board_height, part_widthe part_heightautomaticamente, mas ainda parece errado ter tantos dados duplicados por aí. Além disso, digitar nos campos width/ heightparece um hack.

database-design postgresql
  • 1 1 respostas
  • 442 Views

1 respostas

  • Voted
  1. Best Answer
    Erwin Brandstetter
    2014-02-22T23:34:44+08:002014-02-22T23:34:44+08:00

    Resposta básica

    Sugiro o tipo geométricobox e uma restrição de exclusão (Postgres 9.2+ ). Deve ser a solução perfeita para o seu problema. Ele cria implicitamente um índice GiST que também oferece suporte a determinadas consultas.

    Combine-o com igualdade board_idpara permitir várias placas em uma única tabela. Você precisará do módulo adicional btree_gist. Uma vez por banco de dados:

    CREATE EXTENSION btree_gist;
    

    Para limitar as peças a uma placa, adicione uma CHECKrestrição . Você precisa da caixa confinante do tabuleiro na board_partmesa (redundante). Poderia ficar assim:

    CREATE TABLE board_part (
      board_id bigint NOT NULL REFERENCES board
    , board_box box NOT NULL
    , part_id bigint NOT NULL REFERENCES part
    , part_box box NOT NULL
    , EXCLUDE USING gist (board_id WITH =, part_box WITH &&)
    , CHECK (part_box <@ board_box)
    );
    

    &&.. operador "overlaps"
    <@.. operador "contido"

    A desvantagem: você precisa das dimensões de cada peça e caixa na tabela de forma board_partredundante.

    Resposta avançada

    Para evitar armazenamento redundante, gosto mais dessa nova ideia: IMMUTABLEfunções falsas que retornam a caixa para um id e criam restrições sobre essas funções. Suas tabelas podem se contentar apenas com as colunas de seu design original.

    Fiz um teste completo para verificar se funciona.

    esquema completo

    Tabelas básicas:

    CREATE TABLE part (
      part_id serial PRIMARY KEY
    , part text NOT NULL
    , wide int NOT NULL
    , high int NOT NULL
    );
    
    CREATE TABLE board (
      board_id serial PRIMARY KEY
    , board text NOT NULL
    , wide int NOT NULL
    , high int NOT NULL
    );
    

    IMMUTABLEfunções:

    CREATE OR REPLACE FUNCTION f_boardbox(_board_id int)
      RETURNS box
      LANGUAGE sql IMMUTABLE AS
    'SELECT box(point(0,0), point (b.wide, b.high))
     FROM   public.board b WHERE board_id = $1'; 
    
    CREATE OR REPLACE FUNCTION f_partbox(_part_id int, _wide int, _high int)
      RETURNS box
      LANGUAGE sql IMMUTABLE AS
    'SELECT box(point($2,$3), point($2 + p.wide, $3 + p.high))
     FROM   public.part p WHERE part_id = $1';
    

    Substitua publicpelo esquema real de suas tabelas.
    No Postgres 9.6 ou posterior, adicione PARALLEL SAFE. Ver:

    • Quando marcar funções como PARALLEL RESTRICTED vs PARALLEL SAFE?

    Tabela principal:

    CREATE TABLE board_part (
      board_id int NOT NULL REFERENCES board
    , part_id int NOT NULL REFERENCES part
    , wide int NOT NULL
    , high int NOT NULL
    , EXCLUDE USING gist (board_id WITH =, f_partbox(part_id, wide, high) WITH &&)
    , CHECK (f_partbox(part_id, wide, high) <@ f_boardbox(board_id))
    );
    

    Obviamente, se você atualizar as dimensões para um board_idou part_idem uso, anulará parcialmente o índice e/ou a restrição. Você precisaria recriar qualquer índice ou restrição com base na promessa de que o valor de retorno da IMMUTABLEfunção nunca muda. Ver:

    • O PostgreSQL suporta collations “insensíveis a acentos”?

    No entanto, você pode evitar essa operação cara com gatilhos para atualizar apenas as linhas afetadas:

    Gatilhos

    CREATE OR REPLACE FUNCTION f_board_upaft()
      RETURNS trigger
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       UPDATE board_part
       SET    board_id = board_id        -- enough to trigger CHECK constraint
       WHERE  board_id = NEW.board_id;   -- limit to relevant rows
    
       RETURN NULL;
    END
    $func$;
    
    CREATE TRIGGER board_upaft
    AFTER UPDATE OF wide, high ON board  -- limit to relevant columns
    FOR EACH ROW EXECUTE PROCEDURE f_board_upaft();
    
    
    CREATE OR REPLACE FUNCTION f_part_upaft()
      RETURNS trigger
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       UPDATE board_part
       SET    part_id = 0
       WHERE  part_id = NEW.part_id;
    
       UPDATE board_part
       SET    part_id = NEW.part_id
       WHERE  part_id = 0;               -- enough to update EXCL. constraint
    
       RETURN NULL;
    END
    $func$;
    
    CREATE TRIGGER part_upaft
    AFTER UPDATE OF wide, high ON part
    FOR EACH ROW EXECUTE PROCEDURE f_part_upaft();
    

    part_id = 0é uma linha especial na tabela partcom tamanho 0. Necessária para o gatilho.

    Dados de teste:

    INSERT INTO board (board, wide, high)
    VALUES ('b1010',10,10), ('b2030',20, 30);
    
    INSERT INTO part (part_id, part, wide, high)
    VALUES (0,'p0',0,0);          -- special row needed for trigger!
    
    INSERT INTO part (part, wide, high)
    VALUES ('p11',1,1), ('p33',3,3);
    
    INSERT INTO board_part (board_id, part_id, wide, high)
    VALUES (1,1,3,3)
         , (1,1,5,5)   -- no overlap, inside board
         , (2,1,3,3);  -- different board, no overlap
    

    Teste

    Estes devem gerar exceções se os gatilhos e restrições fizerem seu trabalho:

    UPDATE part SET wide = 6 , high = 6
    WHERE  part_id = 1;            -- violates EXCL. & CHECK constraint
    
    UPDATE part SET wide = 3 , high = 3
    WHERE  part_id = 1;            -- violates EXCL. constraint
    
    UPDATE board SET wide = 2 , high = 2
    WHERE  board_id = 1;            -- violates CHECK constraint
    

    db<>fiddle aqui
    Old sqlfiddle

    • 5

relate perguntas

  • Práticas recomendadas para executar a replicação atrasada do deslocamento de tempo

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

  • Quais são algumas maneiras de implementar um relacionamento muitos-para-muitos em um data warehouse?

  • Sequências Biológicas do UniProt no PostgreSQL

  • 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