curiosidades que você (talvez) não sabia e se sabia vale a pena lembrar
Post on 14-Feb-2017
575 Views
Preview:
TRANSCRIPT
Curiosidades quevocê (talvez) nãosabia e se sabia …
Dickson S. Guedes@guediz
PGBR 2015 - Porto Alegre, RS
O aninhador frenético
ProblemaSELECT ..., CASE WHEN sobrenome IS NULL THEN nome WHEN sobrenome IS NOT NULL THEN sobrenome || ',' || nome ENDFROM ...
SoluçãoSELECT ..., COALESCE(sobrenome || ',', '') || nomeFROM ...
O aninhador frenético II
ProblemaSELECT ..., CASE WHEN COALESCE(endereco, '') <> '' THEN CASE WHEN COALESCE( COALESCE(endereco, '') || ' ' || COALESCE(bairro, '') ) <> ' ' THEN endereco ||' '|| bairro ELSE COALESCE(cidade, '') END ELSE COALESCE(cidade,'SEM CIDADE') ENDFROM ...LEFT JOIN ...LEFT JOIN ...
Possível soluçãoSELECT ..., COALESCE(endereco ||' '|| bairro, cidade, 'SEM CIDADE'),FROM ...LEFT JOIN ...LEFT JOIN ...
Opa! Três parâmetros?SELECT ..., COALESCE(endereco ||' '|| bairro, cidade, 'SEM CIDADE'), ̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂ ̂^̂^̂ ̂ ̂^̂^̂^̂^̂^FROM ...LEFT JOIN ...LEFT JOIN ...
Qual é o resultado disto?SELECT ..., COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, NULL, 'SEM VALOR'), COALESCE(NULL, NULL, 'SEM VALOR'), COALESCE(NULL, 'SEM VALOR'), ...FROM ...
Como isso é possível?
VARIADICCREATE FUNCTION menor_de_todos(VARIADIC valores numeric[])RETURNS numeric AS$$ SELECT min($1[valor]) FROM generate_subscripts($1, 1) g(valor);$$LANGUAGE SQL;
SELECT menor_de_todos(10, 11, 12, 30, -20, -30) as menor;
menor -------- -30(1 row)
Parâmetros! Não é para passar um ARRAYnão!
SELECT menor_de_todos(ARRAY[10, 11, 12, 30, -20, -30]) as menor; ̂^̂^̂^ ̀-- assim da ruim
O esbanjador de SELECTs
Tudo bunitinhoCREATE TYPE situacao_cheque AS ENUM ('compensado', 'devolvido', 'sustado', 'voador');
CREATE TABLE cheque (numero integer not null, valor integer not null, situacao situacao_cheque);
INSERT INTO cheque VALUES (1, 100.00, NULL);
INSERT INTO cheque VALUES (2, 500.00, 'compensado'), (3, 1000.00, 'devolvido'), (4, 900.00, 'devolvido'), (5, 3000.00, 'compensado'), (6, 145.00, 'sustado'), (7, 45.00, 'voador'), (8, 66.00, 'voador'), (9, 96.00, 'sustado'), (10, 45.00, 'sustado');
ai você vê isso …SELECT (SELECT COUNT(*) FROM cheque WHERE situacao = 'compensado') as total_compensados, (SELECT COUNT(*) FROM cheque WHERE situacao = 'devolvido') as total_devolvidos, (SELECT COUNT(*) FROM cheque WHERE situacao = 'voador') as total_voadores;
mas esperava isso …SELECT SUM(CASE WHEN situacao = 'compensado' THEN 1 ELSE 0 END) as total_compensados, SUM(CASE WHEN situacao = 'devolvido' THEN 1 ELSE 0 END) as total_devolvidos, SUM(CASE WHEN situacao = 'voador' THEN 1 ELSE 0 END) as total_voadoresFROM cheque;
Ei! isso também é possívelSELECT SUM( int4( situacao = 'compensado' ) ) as total_compensados, SUM( int4( situacao = 'devolvido' ) ) as total_devolvidos, SUM( int4( situacao = 'voador' ) ) as total_voadoresFROM cheque;
CASTCAST('a' = 'b' AS int4) = 0int4('a' = 'b') = 0
CAST('a' = 'a' AS int4) = 1int4('a' = 'a') = 1
int4('B' = 'B') + int4('A' = 'A') = 2
Outros CASTs que funcionamSELECT text(1); => '1'SELECT date '2014-01-01'; => '2014-01-01'SELECT numeric '1'; => 1SELECT int4 '100000000000000'; => out of rangeSELECT int8 '100000000000000'; => 100000000000000
Então da para fazer algo assimSELECT SUM( int4( situacao = 'compensado' ) ) as total_compensados, SUM( int4( situacao = 'devolvido' ) ) as total_devolvidos, SUM( int4( situacao = 'voador' ) ) as total_voadores ̂^̂^̂ ̂^̂^̂^̂ ,̂̂ ^̂^̂^̂^̂^FROM cheque; ̀~~~~~~~~~~́~~~~~ transforma boolean para integer
Mas qual é "o" problema?todos os cheque acima de R$ 1.000,00 foram compensados?algum cheque abaixo de R$ 800,00 foi devolvido?
Declarativo e explicito MELHOR QUEimplícito.
SELECT BOOL_AND(situacao = 'compensado') FILTER (WHERE valor > 1000) as "todos > 1000 foram compensados?", BOOL_OR(situacao = 'devolvido') FILTER (WHERE valor < 800) as "algum < 800 foi devolvido", EVERY(situacao = 'voador') as "algum cheque voador?"FROM cheque;
SELECT bool_and(situacao = 'compensado') as "todos compensados acima de R$ 1.000"FROM cheque WHERE valor > 1000;
SELECT bool_or(situacao = 'devolvido') as "Algum cheque < 800 devolvido?"FROM chequeWHERE valor < 800;
O explorador do desconhecido
NULL e a aritiméticaSELECT NULL = NULL;SELECT NULL > NULL;SELECT NULL < NULL;
NULL e a aritiméticaSELECT NULL = NULL; => NULLSELECT NULL > NULL; => NULLSELECT NULL < NULL; => NULL
O que é NULL?SELECT NULL IS NULL; => trueSELECT NULL IS DISTINCT FROM NULL; => falseSELECT NULL IS NOT DISTINCT FROM NULL; => true
SELECT 1 IS NULL; => falseSELECT 1 IS DISTINCT FROM NULL; => trueSELECT 1 IS NOT DISTINCT FROM NULL; => false
TESTE DE ATENÇÃO!
Lembram da função menor_de_todos?SELECT menor_de_todos(10, 11, 12, 30, -20, -30) as menor;
menor -------- -30(1 row)
E se eu passar NULL?SELECT menor_de_todos(10, 11, 12, NULL, -20, -30) as menor;
menor -------- ????? (1 row)
-30SELECT menor_de_todos(10, 11, 12, NULL, -20, -30) as menor;
menor -------- -30 (1 row)
menor_de_todos(...) usa a funçãomin(...) que é uma função de agregação
funções de agregação ignoram NULL
O fabricador de registros
Um registro!SELECT ROW(10, 'JOAO', 1500.50);
Dois registros!SELECT ROW(10, 'JOAO', 1500.50) = ROW(10, 'JOAO', 1500.50); => true
SELECT ROW(10, 'JOAO', 1500.50) > ROW(9, 'PEDRO', 500.50); => true
SELECT ROW(1, 'MARIA', 1000.10) > ROW(1, 'MARIA', 1000.11); => false
SELECT ROW(100, 'JOANA', 10.60) < ROW(100, 'JOANA', NULL); => ?????
NULL!
Lembra o INSERT?INSERT INTO ... VALUES (1, 'JOAO', 1500.10);
Pensa no VALUES!/* INSERT INTO ... */ VALUES (1, 'JOAO', 1500.10); -- FUNCIONA!
VALUES é um comando sozinho!INSERT INTO ... SELECT codigo, nome, valor FROM tabela_temporaria;
INSERT INTO ... VALUES (1, 'JOAO', 1500.10);
VALUES (1, '...', 1500.10), (2, '...', 500.00), (3, '...', 100.00), (4, '...', 50.50), (5, '...', 3500.00),;
O masoquista escrivinhador
SIM! Isto funciona; pode testar!SELECT 1 OPERATOR(pg_catalog.+) 2 = 1 + 2; => true
Operadores são funções disfarçadasSELECT 1 + 1; ̂ ̂ ̂__ integer na direita | |____ operador '+' |______ integer na esquerda
SELECT oprname, oprcode, CAST(oprleft AS regtype), -- 23 é integer CAST(oprright AS regtype) -- 23 é integerFROM pg_operator WHERE oprname = '+' AND oprleft = 23 AND oprright = 23;
oprname | oprcode | oprleft | oprright ---------+---------+---------+---------- + | int4pl | integer | integer
Operadores são funções disfarçadasSELECT 1 + 1; ̂ ̂ ̂__ integer na direita | |____ operador '+' |______ integer na esquerda
SELECT oprname, oprcode, CAST(oprleft AS regtype), -- 23 é integer CAST(oprright AS regtype) -- 23 é integerFROM pg_operatorWHERE (oprname, oprleft, oprright) = ('+', 23, 23); ̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^̂^ ̀- este é um outro jeito de escrever o WHERE sem os AND e obter o mesmo resultado...
oprname | oprcode | oprleft | oprright ---------+---------+---------+---------- + | int4pl | integer | integer ̂^̂^̂ .̂___ o postgres chamará esta função para somar integer com integer
Logo …SELECT 1 + 1 = 2;
SELECT int4pl(1, 1) = 2;
Para o infinito, e além
Sim, tudo isso é possívelSELECT '+Infinity'::float > 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; => true
SELECT '-Infinity'::float < 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; => true
Infinito para datas, também, claro!SELECT 'Infinity'::date > current_date; => true
INSERT INTO elemento(nome, validade) VALUES ('urânio', 'Infinity'); isto é um date _.̂^̂^̂^̂^1SELECT 'today'::interval = current_date; => true
SELECT 'yesterday'::date = 'today'::date - interval '1 day'; => true
SELECT 'tomorroy'::date = 'today'::date + interval '1 day'; => true
SELECT current_date + 'allballs'::time; => '2015-09-18 00:00:00'
E tudo pode ser rescrito como …SELECT date 'Infinity' > current_date; => true
INSERT INTO elemento(nome, validade) VALUES ('urânio', 'Infinity'); isto é um date _.̂^̂^̂^̂^
SELECT interval 'today' = current_date; => true
SELECT date 'yesterday' = date 'today' - interval '1 day'; => true
SELECT date 'tomorroy' = date 'today' + interval '1 day'; => true
SELECT current_date + time 'allballs'; => '2015-09-18 00:00:00'
NaNaNaNaNaNaNaNSELECT 'NaN'::numeric + 1; => 'NaN'
Já que estamos falando dedatas
já viram isso?SELECT ... CAST( CAST(ano AS text) || '-' || CAST(mes AS text) || '-' || CAST(dia AS text), AS date) ), ...FROM ...;
ou isso?SELECT ... FROM ...WHERE ... data_nascimento = CAST( CAST(ano AS text) || '-' || CAST(mes AS text) || '-' || CAST(dia AS text), AS date) );;
que se faça a dataSELECT ..., make_date(ano, mes, dia), ...FROM ...;
Algumas fontes são True Type
SERIAL NÃO!
São equivalentesCREATE TABLE tabela ( coluna SERIAL);
OU
CREATE SEQUENCE tabela_coluna_seq;CREATE TABLE tabela ( coluna integer NOT NULL DEFAULT nextval('tabela_coluna_seq'));ALTER SEQUENCE tabela_coluna_seq OWNED BY tabela.coluna;
Chamando funções
Uma função pode ser chamada assimSELECT upper('nome');
upper ------- NOME(1 registro)
Uma função pode ser chamada assimtambém
SELECT * FROM upper('nome');
upper ------- NOME(1 registro)
E também pode ser assimSELECT ('nome').upper;
upper ------- NOME(1 registro)
Uma tabela comum, uma consulta comumCREATE TABLE pessoa(nome text, data_nascimento date);
INSERT INTO pessoa VALUES('JOAO', now() - interval '35 years');
SELECT p.nome, p.data_nascimento FROM pessoa p;
e uma consulta não tanto comumSELECT nome(p), data_nascimento(p) from pessoa p;
nome | data_nascimento ------+----------------- JOAO | 1980-09-18(1 registro)
Mas uma função também pode ter seuretorno como uma tabela
CREATE FUNCTION duplica(IN int, OUT valor_original int, OUT valor_convertido text)AS$$ SELECT $1, CAST($1 AS text) || ' eh texto'$$LANGUAGE SQL;
SELECT * FROM duplica(42);
valor_original | valor_convertido ----------------+------------------ 42 | 42 eh texto(1 registro)
Tempo: 1,684 ms
PSQL
isso ajuda\set v_nome 'JOAO'\set v_idade 18
\connect desenv usuario 192.168.1.1 5432
SELECT * FROM pessoa WHERE nome = :v_nome AND idade > :v_idade \g
\connect teste usuario 192.168.1.2 5432
SELECT * FROM pessoa WHERE nome = :v_nome AND idade > :v_idade ;
isso também ajuda\set v_cpf 1234567890
\connect desenv usuario 192.168.1.1 5432
SELECT nome as v_nome, idade as v_idadeFROM pessoa WHERE cpf = :v_cpf \gset
\connect teste usuario 192.168.1.2 5432
SELECT * FROM pessoaWHERE nome = :v_nome AND idade > :v_idade ;
Talvez num futuro isso funcioneSELECT ... FROM banco.esquema.tabela;
Bom, não é só isso…
ainda tem muito mais coisas…
Visite o IRC, é divertido …
Numa terça-feira monótona, AndrewGierth (a.k.a. RhodiumToad) escreveu isto
SELECT oid, lowrite(lo_open(oid,131072), a)FROM (VALUES (lo_create(0))) v(oid), generate_bytea() as x(a);
-- (note the oid returned)
\lo_export 828133392 'image.png'
SELECT lo_unlink(828133392);
e é isso, obrigado! :)Conferencia Brasileira de PostgreSQL
18 a 20 de Novembro
email: guedes@guedesoft.netgithub: twitter:
http://pgbr.postgresql.org.br/
http://github.com/guedeshttp://twitter.com/guediz
top related