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_type
tabela 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
select whatever from sometable where FKValue in (select FKValue from sometable_2 where other_value = :param)
. Deve ser trivial ver como reafirmar isso com umexists
ou umjoin
.