Pergunta

Eu tenho uma tabela de histórico no SQL Server que basicamente rastreia um item por meio de um processo.O item possui alguns campos fixos que não mudam ao longo do processo, mas possui alguns outros campos, incluindo status e ID, que aumentam à medida que as etapas do processo aumentam.

Basicamente, quero recuperar a última etapa de cada item com uma referência de lote.Então, se eu fizer um

Select * from HistoryTable where BatchRef = @BatchRef

Ele retornará todas as etapas para todos os itens do lote - por exemplo

Id Status BatchRef ItemCount
1       1       Batch001        100
1       2       Batch001        110
2       1       Batch001        60
2       2       Batch001        100

Mas o que eu realmente quero é:

Id Status BatchRef ItemCount
1       2       Batch001        110
2       2       Batch001        100

Editar:Desculpas - não consigo fazer com que as tags TABLE funcionem com Markdown - segui a ajuda ao pé da letra e parece bem na visualização

Foi útil?

Solução

É meio difícil entender o design da sua tabela - acho que SO comeu seus delimitadores.

A maneira básica de lidar com isso é GROUP BY seus campos fixos e selecionar um MAX (ou MIN) para algum valor exclusivo (uma data e hora geralmente funciona bem).No seu caso, eu pensar que GROUP BY seria BatchRef e ItemCount, e Id seria sua coluna exclusiva.

Em seguida, junte-se novamente à tabela para obter todas as colunas.Algo como:

SELECT * 
FROM HistoryTable
JOIN (
   SELECT 
       MAX(Id) as Id.
       BatchRef,
       ItemCount
   FROM HsitoryTable
   WHERE
       BacthRef = @batchRef
   GROUP BY
       BatchRef,
       ItemCount
 ) as Latest ON
   HistoryTable.Id = Latest.Id

Outras dicas

Supondo que você tenha uma coluna de identidade na tabela...

select 
    top 1 <fields> 
from 
    HistoryTable 
where 
    BatchRef = @BatchRef 
order by 
    <IdentityColumn> DESC

Supondo que os IDs dos itens sejam numerados de forma incremental:

--Declare a temp table to hold the last step for each item id
DECLARE @LastStepForEach TABLE (
Id int,
Status int,
BatchRef char(10),
ItemCount int)

--Loop counter
DECLARE @count INT;
SET @count = 0;

--Loop through all of the items
WHILE (@count < (SELECT MAX(Id) FROM HistoryTable WHERE BatchRef = @BatchRef))
BEGIN
    SET @count = @count + 1;

    INSERT INTO @LastStepForEach (Id, Status, BatchRef, ItemCount)
        SELECT Id, Status, BatchRef, ItemCount
        FROM HistoryTable 
        WHERE BatchRef = @BatchRef
        AND Id = @count
        AND Status = 
        (
            SELECT MAX(Status) 
            FROM HistoryTable 
            WHERE BatchRef = @BatchRef 
            AND Id = @count
        )

END

SELECT * 
FROM @LastStepForEach
SELECT id, status, BatchRef, MAX(itemcount) AS maxItemcount 
FROM HistoryTable GROUP BY id, status, BatchRef 
HAVING status > 1 

É um pouco difícil decifrar seus dados da maneira como o WMD os formatou, mas você pode usar o tipo de truque necessário com expressões de tabela comuns no SQL 2005:

with LastBatches as (
    select Batch, max(Id)
    from HistoryTable
    group by Batch
)
select *
from HistoryTable h
    join LastBatches b on b.Batch = h.Batch and b.Id = h.Id

Ou uma subconsulta (supondo que o grupo na subconsulta funcione - de cara, não me lembro):

select *
from HistoryTable h
    join (
        select Batch, max(Id)
        from HistoryTable
        group by Batch
    ) b on b.Batch = h.Batch and b.Id = h.Id

Editar:Eu estava presumindo que você queria o último item para todo lote.Se você precisar apenas de um lote, as outras respostas (fazer o primeiro e ordenar em ordem decrescente) são o caminho a seguir.

Como já sugerido, você provavelmente deseja reordenar sua consulta para classificá-la na outra direção, para realmente buscar a primeira linha.Então você provavelmente gostaria de usar algo como

SELECT TOP 1 ...

se você estiver usando MSSQL 2k ou anterior, ou a variante compatível com SQL

SELECT * FROM (
  SELECT
    ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
    columns
  FROM tablename
) AS foo
WHERE rownumber = n

para qualquer outra versão (ou para outros sistemas de banco de dados que suportem a notação padrão), ou

SELECT ... LIMIT 1 OFFSET 0

para algumas outras variantes sem o suporte SQL padrão.

Veja também esse pergunta para alguma discussão adicional sobre a seleção de linhas.Usar a função agregada max() pode ou não ser mais rápido, dependendo se o cálculo do valor requer uma varredura da tabela.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top