O SQL é declarativo?


22

Eu pergunto porque muitas das perguntas que vejo no SQL são: "Isso é lento. Como faço para acelerar"? Ou há tutoriais dizendo "Faça desta maneira e não dessa maneira, pois é mais rápido".

Parece-me que grande parte do SQL sabe exatamente como uma expressão seria executada e, a partir desse conhecimento, escolhe estilos de expressão com melhor desempenho. Isso não corresponde a um aspecto da programação declarativa - o de deixar o sistema para decidir qual a melhor maneira de executar o cálculo, especificando apenas o que o cálculo deve produzir.

Um mecanismo SQL não deveria se importar com o uso in, existsou joinse é realmente declarativo, não deveria apenas fornecer a resposta correta em tempo razoável, se possível por qualquer um dos três métodos? Este último exemplo é solicitado por este post recente, que é do tipo mencionado no meu parágrafo de abertura.

Índices

Eu acho que o exemplo mais fácil que eu poderia ter usado está relacionado à criação de um índice para uma tabela. O gumph aqui no w3schools.com até tenta explicar isso como algo invisível para o usuário que está lá por razões de desempenho. Sua descrição parece colocar índices SQL no campo não declarativo e eles são adicionados rotineiramente à mão por motivos puramente de desempenho.

É o caso deles estarem em algum lugar um banco de dados SQL ideal que seja muito mais declarativo do que todo o resto, mas porque é bom que não se ouça sobre isso?


@FrustratedWithFormsDesigner: Eu sei exatamente o que isso significa. select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param). Deve ser trivial ver como reafirmar isso com um existsou um join.
Mason Wheeler

Usando raciocínio semelhante, acho que expressões regulares são um método de expressão mais declarativo, pois raramente vejo perguntas de desempenho respondidas por "você deve escrever dessa maneira para obter melhor desempenho". Estou destruindo meu cérebro e meio que me lembro de alguma pergunta relacionada a afirmações negativas de antecipação ou antecipação em um regexp lento, onde a resposta era reescrever o regexp de uma maneira diferente para fazer o mesmo em menos tempo.
precisa saber é o seguinte

O desempenho é um detalhe de implementação. O desempenho de praticamente qualquer implementação IN poderia ser comparável ou melhor que EXISTS e JOIN se os desenvolvedores do processador de consultas considerassem uma prioridade.
23413 JustinCelebrC

1
@ JustinC, parece ser mais do que um detalhe, dada a preponderância de perguntas e dicas sobre SQL orientadas para o desempenho de uma linguagem supostamente declarativa?
precisa saber é o seguinte

Não há uma definição clara de uma linguagem de programação declarativa e, portanto, não faz sentido falar sobre isso. Alguns idiomas são de nível superior ao de outros, só isso.
gardenhead

Respostas:


21

SQL é teoricamente declarativo. Mas você sabe o que eles dizem sobre a diferença entre teoria e prática ...

Em sua essência, o conceito de "programação declarativa" nunca foi realmente eficaz e provavelmente nunca será até que tenhamos um compilador baseado em IA capaz de examinar o código e responder à pergunta "qual é a intenção desse código?" inteligentemente, da mesma maneira que a pessoa que o escreveu. No coração de toda linguagem declarativa há um monte de códigos imperativos tentando freneticamente resolver esse problema sem a ajuda de uma IA.

Muitas vezes, funciona surpreendentemente bem, porque os casos mais comuns são casos comuns , que as pessoas que escreveram a implementação da linguagem conheciam e encontravam boas maneiras de lidar. Mas, então, você se depara com um caso delicado que o implementador não considerou e vê o desempenho diminuir rapidamente, à medida que o intérprete é forçado a levar o código muito mais literalmente e manipulá-lo de maneira menos eficiente.


3
Nunca é verdadeiramente eficaz? Linguagem SQL, LINQ, Knockout.js, Prolog, ELM. Você pode querer verificar novamente. Estou usando principalmente tecnologias declarativas no momento.
brian

5
@ brian: E todos eles se degeneram rapidamente quando você se depara com um caso que ninguém pensou. Suponho que deveria ter dito "nunca verdadeiramente eficaz no caso geral ".
Mason Wheeler

Quando sua resposta está definida para degradar, visto como é armazenada em um banco de dados SQL Server? :) Eu raramente encontro um caso de vantagem em nenhum deles que não possa ser resolvido dentro da estrutura. Vejo de onde você vem, mas os casos extremos realmente não me causam muita dor, pelo quão benéfico e fácil raciocinar cerca de 99% do código declarativo é. É como dizer que Clojure ou F # é ruim porque você teve que usar um tipo mutável para resolver seu problema.
brian

11
@ brian: I rarely hit an edge case in any of them that couldn't be solved within the framework.Sim, esse é o ponto: ter que descobrir uma maneira de resolvê-los dentro da estrutura, porque a estrutura não é inteligente o suficiente para resolvê-la da maneira que você a declarou originalmente.
Mason Wheeler

Que tal selecionar ... para atualização? Parece um comando imperativo.
Jesvin Jose

6

Eu estava pensando nisso alguns dias atrás, após uma otimização do SQL. Acho que podemos concordar que SQL é uma "linguagem declarativa" na definição da Wikipedia:

Paradigma de programação que expressa a lógica da computação sem descrever seu fluxo de controle

Se você pensa quantas coisas são feitas atrás das cortinas (olhando as estatísticas, decidindo se um índice é útil, indo para uma junção aninhada, mesclada ou hash, etc. etc), devemos admitir que fornecemos apenas um nível alto lógica e o banco de dados cuidou de toda a lógica do fluxo de controle de baixo nível.

Também nesse cenário, algumas vezes o otimizador de banco de dados precisa de algumas "dicas" do usuário para fornecer os melhores resultados.

Outra definição comum de linguagem "declarativa" é (não consigo encontrar uma fonte autorizada):

Paradigma de programação que expressa o resultado desejado da computação sem descrever as etapas para alcançá-lo (também abreviado com "descreva o que, não como")

Se aceitarmos essa definição, encontraremos os problemas descritos pelo OP.

A primeira questão é que o SQL nos fornece várias maneiras equivalentes de definir "o mesmo resultado". Provavelmente, esse é um mal necessário: quanto mais poder expressivo dermos a uma linguagem, maior a probabilidade de haver maneiras diferentes de expressar a mesma coisa.

Como exemplo, fui solicitado uma vez a otimizar esta consulta:

 SELECT Distinct CT.cust_type,  ct.cust_type_description 
   from customer c 
              INNER JOIN 
              Customer_type CT on c.cust_type=ct.cust_type;

Como os tipos eram muito menores que o cliente e havia um índice na cust_typetabela de clientes, consegui uma grande melhoria reescrevendo-o como:

 SELECT CT.cust_type,  ct.cust_type_description 
   from Customer_type CT
  Where exists ( select 1 from customer c 
                  Where c.cust_type=ct.cust_type);

Nesse caso específico, quando perguntei ao desenvolvedor o que ele queria alcançar, ele me disse: "Eu queria todos os tipos de clientes para os quais tinha pelo menos um cliente"; aliás, é exatamente assim que a consulta do otimizador pode ser descrita.

Portanto, se eu poderia encontrar uma consulta equivalente e mais eficiente, por que o otimizador não pode fazer o mesmo?

Meu melhor palpite é que é por duas razões principais:

SQL expressa lógica:

Como o SQL expressa a lógica de alto nível, realmente queremos que o otimizador "nos engane" e a nossa lógica? Eu gritava entusiasticamente "sim" se não fosse por todas as vezes que eu tive que forçar o otimizador a escolher o caminho de execução mais eficiente. Eu acho que a idéia poderia ser permitir que o otimizador faça o melhor (também revise nossa lógica), mas nos dê um "mecanismo de dica" para ajudar quando algo ficar louco (seria como ter a roda + freios um carro autônomo).

Mais opções = mais tempo

Mesmo o melhor otimizador de RDBMS não testa TODOS os caminhos de execução possíveis, pois devem ser muito rápidos: quão bom seria otimizar uma consulta de 100ms a 10ms se eu precisar gastar cada 100ms escolhendo o melhor caminho? E isso é com o otimizador respeitando nossa "lógica de alto nível". Se ele também testar todas as consultas SQL equivalentes, o tempo do otimizador poderá aumentar várias vezes.

Outro bom exemplo de reescrita de consulta que nenhum RDBMS é realmente capaz de fazer é (a partir desta postagem interessante no blog )

SELECT t1.id, t1.value, SUM(t2.value)
  FROM mytable t1
       JOIN mytable t2
         ON t2.id <= t1.id
 GROUP BY t1.id, t1.value;

pode ser escrito como este (funções analíticas necessárias)

 SELECT id, value, SUM(t1.value) OVER (ORDER BY id)
   FROM mytable

1
O exemplo de reescrever a junção para um existe é interessante. Uma regra prática que tento impressionar os desenvolvedores de SQL é que o uso do DISTINCT é um cheiro de código - a consulta ou o modelo de dados está possivelmente errado e uma abordagem diferente deve ser buscada.
David Aldridge
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.