Eu tenho a seguinte consulta no msaccess
SELECT Trim([T13_RefSupplier_France_List].[Supplier_code]) AS Supplier_code,
Trim([T13_RefSupplier_France_List].[Art]) AS Internal_reference,
Trim([fp_rcli]) AS Supplier_reference,
Last(Trim([ar_fami])) AS Family_code,
Last(Trim([fp_upri])) AS Purchasing_unit,
Last(Nz([fp_pcde],0)) AS Purchasing_price,
Last(Supplier_currency_France.Recode) AS Purchasing_currency,
Last("") AS Consigned,
Last(0) AS Eco_order_qty,
Last(CDbl(Nz(Nz([fp_cond],[ar_qcdi]),0))) AS Pack_order_qty,
Last(IIf([fp_minc]=0,Nz([fp_cond],[ar_qcdi]),[fp_minc])) AS Min_order_qty,
0 AS Min_order_value,
0 AS Product_grossweight,
0 AS Product_grosscube,
Last(gpfprodu_France.fp_dela) AS Leadtime_days,
Nz([Localisation_France].[Site],"Poitiers") AS Site,
CDbl(Nz([Active],-1)) AS Supplier_active,
Max(IIf([ar1_pdanz]=0,NULL,[ar1_pdanz])) AS Ref_price
FROM ((T13_RefSupplier_France_List
LEFT JOIN ((gparticl_France
LEFT JOIN Localisation_France ON gparticl_France.ar_loco=Localisation_France.Localisation)
LEFT JOIN [*gpartic1_France] ON gparticl_France.ar_code=[*gpartic1_France].ar1_code) ON T13_RefSupplier_France_List.Art=gparticl_France.ar_code)
LEFT JOIN ((gpfprodu_France
LEFT JOIN gpfourni_France ON gpfprodu_France.fp_four=gpfourni_France.fo_code)
LEFT JOIN Supplier_currency_France ON gpfourni_France.fo_monn=Supplier_currency_France.fo_monn) ON (T13_RefSupplier_France_List.Supplier_code=gpfprodu_France.fp_four)
AND (T13_RefSupplier_France_List.Art=gpfprodu_France.fp_arti))
LEFT JOIN T13_RefSupplier_France_SupplierActive ON (T13_RefSupplier_France_List.Art=T13_RefSupplier_France_SupplierActive.Art)
AND (T13_RefSupplier_France_List.Supplier_code=T13_RefSupplier_France_SupplierActive.Supplier_code)
GROUP BY Trim([T13_RefSupplier_France_List].[Supplier_code]),
Trim([T13_RefSupplier_France_List].[Art]),
Trim([fp_rcli]),
Nz([Localisation_France].[Site],"Poitiers"),
CDbl(Nz([Active],-1))
HAVING (((Trim(T13_RefSupplier_France_List.Supplier_code))<>"FG0002")
AND ((Trim(T13_RefSupplier_France_List.Art))<>"A60101000000000"));
Eu posso fazer uma GROUP BY
sem usar a coluna que tem a LAST
função mas quando eu removo a função LAST, tenho que adicionar todas as colunas na GROUP BY
(veja abaixo).
SELECT Trim([T13_RefSupplier_France_List].[Supplier_code]) AS Supplier_code,
Trim([T13_RefSupplier_France_List].[Art]) AS Internal_reference,
Trim([fp_rcli]) AS Supplier_reference,
Trim([ar_fami]) AS Family_code,
Trim([fp_upri]) AS Purchasing_unit,
Nz([fp_pcde],0) AS Purchasing_price,
Supplier_currency_France.Recode AS Purchasing_currency,
"" AS Consigned,
0 AS Eco_order_qty,
CDbl(Nz(Nz([fp_cond],[ar_qcdi]),0)) AS Pack_order_qty,
IIf([fp_minc]=0,Nz([fp_cond],[ar_qcdi]),[fp_minc]) AS Min_order_qty,
0 AS Min_order_value,
0 AS Product_grossweight,
0 AS Product_grosscube,
gpfprodu_France.fp_dela AS Leadtime_days,
Nz([Localisation_France].[Site],"Poitiers") AS Site,
CDbl(Nz([Active],-1)) AS Supplier_active,
Max(IIf([ar1_pdanz]=0,NULL,[ar1_pdanz])) AS Ref_price
FROM ((T13_RefSupplier_France_List
LEFT JOIN ((gparticl_France
LEFT JOIN Localisation_France ON gparticl_France.ar_loco=Localisation_France.Localisation)
LEFT JOIN [*gpartic1_France] ON gparticl_France.ar_code=[*gpartic1_France].ar1_code) ON T13_RefSupplier_France_List.Art=gparticl_France.ar_code)
LEFT JOIN ((gpfprodu_France
LEFT JOIN gpfourni_France ON gpfprodu_France.fp_four=gpfourni_France.fo_code)
LEFT JOIN Supplier_currency_France ON gpfourni_France.fo_monn=Supplier_currency_France.fo_monn) ON (T13_RefSupplier_France_List.Supplier_code=gpfprodu_France.fp_four)
AND (T13_RefSupplier_France_List.Art=gpfprodu_France.fp_arti))
LEFT JOIN T13_RefSupplier_France_SupplierActive ON (T13_RefSupplier_France_List.Art=T13_RefSupplier_France_SupplierActive.Art)
AND (T13_RefSupplier_France_List.Supplier_code=T13_RefSupplier_France_SupplierActive.Supplier_code)
GROUP BY Trim([T13_RefSupplier_France_List].[Supplier_code]),
Trim([T13_RefSupplier_France_List].[Art]),
Trim([fp_rcli]),
Nz([Localisation_France].[Site],"Poitiers"),
Trim([ar_fami]),
Trim([fp_upri]),
Nz([fp_pcde],0),
Supplier_currency_France.Recode,
"",
0,
CDbl(Nz(Nz([fp_cond],[ar_qcdi]),0)),
IIf([fp_minc]=0,Nz([fp_cond],[ar_qcdi]),[fp_minc]),
0,
0,
0,
gpfprodu_France.fp_dela,
CDbl(Nz([Active],-1))
HAVING (((Trim(T13_RefSupplier_France_List.Supplier_code))<>"FG0002")
AND ((Trim(T13_RefSupplier_France_List.Art))<>"A60101000000000"));
As perguntas são duplas:
- Qual é o comportamento da
LAST
função? - Digamos que eu queira transpor a
LAST
função para SQLServer: O que devo fazer?
Obrigado
Da documentação :
Isso não diz nada sobre o comportamento de
LAST
quando umaGROUP BY
cláusula está presente, mas ao testar parece queFIRST
eLAST
retorna valores da linha encontrada primeiro ou por último em cada grupo.Sem uma
ORDER BY
cláusula, a linha escolhida (por grupo) porFIRST
eLAST
é essencialmente arbitrária. O ponto importante é que os valores escolhidos pelas funções multipleFIRST
e virão da mesma linha .LAST
Este último ponto significa que você não pode simplesmente substituir
FIRST
ouLAST
porMIN
ouMAX
(além das diferentes semânticas), porque o mínimo e o máximo geralmente não serão da mesma linha de origem.É essencialmente impossível duplicar isso exatamente, pois o comportamento do Access não é definido com precisão; não é possível prever qual linha será escolhida como
FIRST
ouLAST
em todos, exceto nos casos mais simples.Dito isso, se você puder melhorar a semântica da consulta para ter uma opção determinística para
FIRST
ouLAST
linha dentro de cada grupo, uma tradução geral seria numerar cada linha (crescente ou decrescente por grupo) e, em seguida, escolha valores da linha numerada 1.A numeração das linhas pode ser feita com
ROW_NUMBER
. Dentro daOVER
cláusula, asGROUP BY
colunas vão naPARTITION BY
seção, com ordenação determinística fornecida naORDER BY
seção. Você precisará escrever uma subconsulta ou usar uma expressão de tabela comum (CTE) para filtrar o número da linha para 1.Por exemplo:
Demonstração:
db<>fiddle
No SQL Server 2012 ou posterior, isso também pode ser feito com as funções
FIRST_VALUE
e emLAST_VALUE
janela, mas o plano de execução pode ser menos eficiente. Além disso, essas funções em janela não são agregadas, portanto, você precisa escrever a expressão para que ela retorne o mesmo valor para cada linha por grupo e, em seguida, aplicar uma agregação arbitrária. Por exemplo (usando ordenação não determinística apenas para variedade):Demonstração:
db<>fiddle
Sua terceira opção é escrever um agregado definido pelo usuário (UDA) SQLCLR. Atualmente, não é possível garantir a ordenação determinística com eles, mas a implementação pode corresponder mais ao que o Access faz. Você precisaria ter cuidado para que todos os resultados UDA fossem calculados pelo mesmo operador, para garantir que os resultados de várias chamadas UDA venham da mesma linha de origem.
RESUMO
GROUP BY
, você só pode incluir as colunas naGROUP BY
cláusula, constantes e valores agregados.LAST_VALUE()
, mas só existe no SQL Server 2012 ou posterior.EXPLICAÇÃO DETALHADA
LAST()
é o que chamamos de função agregada . As funções agregadas calculam um valor para todas as linhas em uma consulta (se não houverGROUP BY
, ou para todas as linhas com os mesmos valores para as colunas naGROUP BY
cláusula. No SQL Server (e, suponho, no Access), se você usar um função agregada em suaSELECT
lista (a lista de colunas de dados a serem retornadas da consulta) em uma consulta semGROUP BY
cláusula, então tudo que está sendo retornado deve ser:Consigned
ouMin_order_value
em sua consultaNOW()
no Access ouGETDATE()
no SQL ServerLAST()
funções de sua primeira consulta ouMax(IIf([ar1_pdanz]=0,NULL,[ar1_pdanz]))
em ambas as consultas.Se você tiver uma
GROUP BY
cláusula, há uma adição ao acima: aSELECT
lista também pode incluir as colunas (ou cálculos) listadas naGROUP BY
cláusula. Geralmente, eles precisam aparecer exatamente da mesma forma nos dois lugares (por exemplo, se vocêGROUP BY SUBSTRING(field1,1,20)
, não pode ter apenasfield1
naSELECT
lista).Quando você coloca
Last(Trim([ar_fami]))
em suaSELECT
lista, você está basicamente dizendo ao Access: "Haverá vários valores paraar_fami
no meu grupo, mas eu realmente não me importo com quais são esses valores; apenas me dê o valor da última linha em que você acontece ver."Se você incluir
Trim([ar_fami])
naSELECT
lista e no arquivoGROUP BY
, estará informando ao Access que deseja ver cada valor exclusivo em sua saída e os subtotais/médias/qualquer coisa desses valores, juntamente com o restante das colunas no arquivoGROUP BY
.Quando você tenta incluir
Trim([ar_fami])
naSELECT
lista, mas não noGROUP BY
, o Access fica confuso: você pode ter vários valores para cada combinação possível dasGROUP BY
colunas e não tem como dizer qual desses valores você gostaria de ver.Nota para nós humanos: na prática, é possível que para todas as linhas em cada um de seus grupos, na realidade todos os valores sejam exatamente os mesmos. No entanto, os bancos de dados geralmente precisam decidir como obterão os dados antes de realmente começarem a obtê-los; e, nesse ponto, não há como saber se todos os
ar_fami
valores de cada grupo serão iguais. Então, tem que saber lidar com valores diferentes com antecedência.By the way - você deve ser capaz de tirar as constantes da
GROUP BY
cláusula em sua segunda consulta (o""
e os múltiplos0
s). Como são sempre iguais, não podem alterar o agrupamento das linhas.Quanto à sua segunda pergunta, a partir do SQL Server 2012, você tem acesso a uma função semelhante lá. O nome é um pouco diferente:
LAST_VALUE()
em vez deLAST()
. Além disso,LAST_VALUE()
é uma "função de janela"; ele requer umaOVER()
cláusula, definindo os grupos com os quais trabalha e a ordem que usará, e os resultados são calculados para cada linha no conjunto de resultados, não para um grupo inteiro.Você teria que criar uma consulta complicada (ou, mais provavelmente, um conjunto de consultas aninhadas) para capturar valores não agrupados do último registro visto em cada grupo.
Na prática, esses valores parecem ser de utilidade limitada. Se você está agregando o total de vendas dos vendedores em quatro locais diferentes, realmente importa que o nome do último vendedor tenha sido "Tony Warner"? Especialmente porque é possível que você execute a mesma consulta dois dias depois, com os mesmos dados subjacentes, gerando os mesmos resultados de grupo para totais e subtotais - mas observando que o nome do último vendedor era "Frank Northrup"? Pelo menos no SQL Server, o SQL classificará os dados em vários grupos para obter os valores agregados e aplicará o
ORDER BY
que você especificou; mas depende de você qual é essa ordem. Se oORDER BY
não inclui a coluna da qual você está obtendo o último valor ou algo que está vinculado a um valor específico, a "última" linha pode ser diferente cada vez que você chama a função, mesmo no mesmo conjunto de dados.Ainda assim, obviamente, algumas pessoas acham que vale a pena usar. Eu posso suspeitar que essas pessoas realmente não entendem o que estão recebendo de volta, ou (pior) o que as pessoas olhando para seus resultados vão supor, mas talvez haja algum valor definido para comandar que eu simplesmente não estou vendo. Para mim, se os resultados são basicamente aleatórios, não vejo o valor.