Redirect known folders to OneDrive for Business

Redirect known folders to OneDrive for Business

This article is for IT admins managing the OneDrive sync client in a Windows Server enterprise environment that uses Active Directory Domain Services. You can redirect Windows known folders (such as Documents) to the OneDrive for Business sync location for the users in your domain.

There are two primary advantages to doing this:

  • Your users can continue using their Documents and other similar folders. They don’t have to change their daily work habits to use OneDrive.
  • Using OneDrive for your documents gives you a backup of your data in the cloud and gives you access to your documents from any device.

For these reasons, we recommend setting up folder redirection if you’re an enterprise or large organization. Small or medium businesses may also find this useful, but keep in mind you’ll need some experience with Group Policy and data migration to follow the procedures below.

How it works

Here’s an overview of what we’ll be configuring in this article:

  1. We’ll set a policy at the domain level to make sure users all sync to the same folder when they install the OneDrive sync client.
  2. We’ll set additional policies to redirect the Documents folder to that sync location.

When users sign in on their computers, Group Policy will detect if the OneDrive folder exists in the specified location. If the folder doesn’t exist (for example, if the user hasn’t set up the sync client yet or if this is the first time they’ve signed in on the computer), the Documents folder will not be redirected. If the folder does exist, the Documents folder will be redirected to the OneDrive folder.

Once the Documents folder has been redirected, shortcuts to Documents – such as those in File Explorer – will point to the new location. However, the original folder and its contents will remain at its previous location under %userprofile%\Documents. You can then migrate any files from the original folder to OneDrive, and delete the original folder if desired.

Before you begin

There are a number of things to consider in determining if redirecting known folders to OneDrive is a good solution for your organization:

  • OneDrive has some restrictions around file naming, file size, and file type, that you should review before deploying new OneDrive sync client. Keep in mind that your users may try to use files of these types with OneDrive, or the OneDrive sync client may try to sync them when you redirect users’ Documents folders.
  • If your users have used the Documents folder as an installation location for some legacy applications, the applications may no longer work after the folder has been redirected. If your organization uses legacy applications that were not written to support folder redirection, be sure to test them before redirecting folders to OneDrive.
  • If your users’ Documents folders contain items with a very high frequency of updates – such as databases, web servers, or Outlook OST files – we recommend not redirecting these folders to OneDrive. While such files should continue to function normally, the high frequency of sync activity due to constantly changing files may cause network and performance issues.

If you’re already redirecting known folders to a different location – for example, a network share – or if you have already deployed the new OneDrive sync client, then there are some additional planning considerations:

  • If known folders are currently redirected to a network share or other location, you will need to migrate the data from that location to OneDrive after you redirect the known folders to OneDrive.
  • After you redirect known folders to OneDrive using the procedures in this article, existing files in known folders will remain on users’ computers and need to be manually moved to each user’s OneDrive folder. We suggest using XCopy or Robocopy scripting as an automated way of moving the files for your users.
  • To redirect known folders to OneDrive, users must be syncing their OneDrive files to the default location (%userprofile%\OneDrive – <TenantName>). Known folders can’t be redirected to a different location.
  • If your users have a large amount of data that will be uploaded to OneDrive, you may want to stage your deployment to limit the impact on your network.

Prerequisites and baseline environment

The procedures in this article require a particular existing configuration to exist in order to work. Check these prerequisites before you get started:

  • Make sure you installed the OneDrive for Business Group Policy objects on your domain, and updated the ADMX file with your tenant ID.
  • Make sure any existing OneDrive users in your organization are syncing their files to the default sync location (%userprofile%\OneDrive – <TenantName>). Users who are syncing files to a different location will not have their known folders redirected to OneDrive.
  • All of the procedures in this article are performed in your domain Group Policy editor. You need to be a domain administrator to perform these procedures.

Redirecting known folders to OneDrive

The first step in redirecting known folders to OneDrive is to make sure users sync their OneDrive to the default location when they set up the sync client. We do this through a domain policy.

To prevent users from changing the location of their OneDrive folder

  1. In your domain Group Policy editor, under User Configuration\Policies\Administrative Templates\OneDrive, double-click Prevent users from changing the location of their OneDrive folder.
  2. Select the Enabled option, and then click OK.

The next step is to create an environment variable for the OneDrive folder. Group Policy won’t let us redirect known folders directly to a different location under %userprofile%, so we need to create a new environment variable that contains the location of the folder under %userprofile%.

We’ll use item-level targeting in this environment variable to prevent folders from being redirected until the folder has been created by the sync client.

To create an environment variable for the OneDrive folder

  1. In your domain Group Policy editor, under User Configuration\Preferences\Windows Settings, right-click Environment, click New, and then click Environment Variable.
  2. In the Name box, type OneDriveSync.
  3. In the Value box, type %userprofile%\<SyncFolder>.

    <SyncFolder> is the name of your default folder. For example, OneDrive – Contoso   .

  4. On the Common tab, select the Item-level targeting check box, and then click Targeting.
  5. In the Targeting Editor, click New Item, and then click File Match.
  6. Choose Folder exists from the Match type drop down list.
  7. In the Path box, type %userprofile%\<SyncFolder> (the same path that you used in step 3).
  8. Click OK.
  9. Click OK.

Now it’s time to configure the known folders redirection itself.

Note that we do not support having existing content automatically migrated by Group Policy to the OneDrive folder. (See the warning in the procedure below). With automatic file migration, there is a potential for data loss in cases where there are files in both locations that have matching file names.

To redirect Documents folders to OneDrive

  1. In your domain Group Policy editor, under User Configuration\Policies\Windows Settings\Folder Redirection, right-click Documents and click Properties.
  2. From the Settings drop down list, choose Basic – Redirect everyone’s folder to the same location.
  3. Under Target folder location, choose Redirect to the following location.
  4. In the Root Path box, type %OneDriveSync%\Documents.
  5. On the Settings tab, clear the Move the contents of Documents to the new location check box.

    WARNING: Leaving this setting enabled could result in data loss when the contents of the Documents folder is merged with the OneDrive folder, if there are files with the same name in both locations.

  6. Click OK.

We’ve now set the Documents folder to redirect to the OneDrive folder. You can also easily redirect related known folders – Picture, Music, or Videos – by having them follow the redirection of the Documents folder. If you want to do this, use the following procedure.

To redirect related folders to the OneDrive folder

  1. In your domain Group Policy editor, under User Configuration\Policies\Windows Settings\Folder Redirection, right-click the related folder that you want to redirect – Pictures, Music, or Videos – and click Properties.
  2. In the Setting drop down list, choose Follow the Documents folder.
  3. Click OK.

Now that folder redirect has been configured, users’ known folders will be redirected to their OneDrive folder once their OneDrive sync client has been set up. Once the redirect is in place, you’ll need to migrate the user’s data from the original location on their local disk to the OneDrive folder.

Keep in mind that as new users and computers come online over time, users may still save files to their Documents folder before they configure the OneDrive sync client, and these files would then need to be moved to the OneDrive folder after the redirect takes place.

See Also

How to See Which Group Policies Are Applied to Your PC and User Account

How to See Which Group Policies Are Applied to Your PC and User Account

How to See Which Group Policies Are Applied to Your PC and User Account

By Walter Glenn on May 31st, 2017

We have shown you a lot of tips and tricks over the years that involve modifying Local Group Policy. If you would ever like to see all the Group Policy settings in effect on your PC, here’s how to do it.

In the Windows world, Group Policy provides a way for network administrators to assign specific settings to groups of users or computers. Those settings then get applied whenever a user in the group logs in to a networked PC or whenever a PC in the group is started. Local Group Policy is a slightly more limited version that applies settings only to a local computer or users—or even a group of local users. We’ve featured a number of tricks here in the past that use Local Group Policy to change settings that you can’t change anywhere else—except by editing the Windows Registry. If you’re in the habit of changing Local Group Policy settings, you might find it useful to see all the changes you’ve made in one place, rather than digging through the Local Group Policy Editor.

 

Note: Local Group Policy is only available in the Professional and Enterprise versions of Windows. If you’re using a Home edition, you won’t have access to the Local Group Policy Editor.

View Applied Policies with the Resultant Set of Policy Tool

The easiest way to see all the Group Policy settings you’ve applied to your PC or user account is by using the Resultant Set of Policy tool. It doesn’t show every last policy applied to your PC—for that you’ll need to use the Command Prompt, as we describe in the next section. However, it does show pretty much all the policies you will have set for regular use. And it provides a simple, graphical interface for browsing through the Group Policy settings currently in effect on your PC—whether those settings come from Group Policy or Local Group Policy.

To open the tool, hit Start, type “rsop.msc,” and then click the resulting entry.

The Resultant Set of Policy tool starts by scanning your system for applied Group Policy settings.

After it’s done scanning, the tool shows you a management console that looks very much like the Local Group Policy Editor—except that it only displays enabled settings along with a few unconfigured security settings.

This makes it easy to browse through and see what policies are in effect. Note that you can’t use the Resultant Set of Policy tool to change any of these settings. You can double-click a setting to view details, but if you want to disable or make changes to a setting, you’ll have to use the Local Group Policy Editor.

View Applied Policies with the Command Prompt

If you’re comfortable using the Command Prompt, it does provide a couple of advantages over using the Resultant Set of Policy tool. First, it can show every last policy in effect on your PC. Second, it will show some additional security information—like what security groups a user is part of or what privileges they have.

To do this, we’ll be using the gpresult command. You must specify a scope for the results, and valid scopes include “user” and “computer.” This means that to see all the policies in effect for the user and the PC, you’ll have to run the command twice.

To view all the policies applied to the user account you’re currently logged in with, you would use the following command:

The /v parameter in that command specifies verbose results, so you’ll see everything. Scroll down a bit and you’ll see a section named “Resultant Set Of Policies for User,” which contains the information you’re after.

If you’re looking for all policies applied to your Computer, all you need to do is change the scope:

If you scroll down, you’ll see that there is now a Resultant Set Of Policies for Computer section.

 

Como retornar o último registro por grupo ?

Boa Noite Pessoal,

Eu tinha outros planos para a próxima postagem, mas ultimamente nos fóruns da Microsoft e nas comunidades do Orkut, estou vendo uma dúvida com uma recorrência enorme. A dúvida muda um pouco o enunciado cada vez que ela aparece, mas a essência é basicamente a mesma. “Quero selecionar 1 registro de cada Codigo“, “Select – Data mais recente“, “Buscar mais de um campo de tabela agrupando somente por um campo“, etc. Esse problema na verdade é uma versão mais específica do “TOP N” que descrevi em um dos meus Webcast e cuja a solução está disponível no blog (Dicas e Truques sobre consultas complexas no SQL Server).

Vou descrever melhor o problema e sua solução através de um exemplo. O script abaixo, cria uma tabela com alguns registros de lancamentos de conta corrente.

— Cria a tabela de clientes
CREATE TABLE Clientes (
IDCliente INT, NomeCliente VARCHAR(50))

— Popula a tabela de Clientes
INSERT INTO Clientes VALUES (1, ‘Bianca’)
INSERT INTO Clientes VALUES (2, ‘Herbert’)
INSERT INTO Clientes VALUES (3, ‘Paloma’)
INSERT INTO Clientes VALUES (4, ‘Marcos’)
INSERT INTO Clientes VALUES (5, ‘Jonas’)
INSERT INTO Clientes VALUES (6, ‘Tatiana’)

— Cria a tabela de saldos
CREATE TABLE Lancamentos (
IDCliente INT, Data SMALLDATETIME,
Valor SMALLMONEY, Tipo CHAR(1))

— Popula a tabela de Saldos
INSERT INTO Lancamentos VALUES (1, ‘20090109’,15.00,‘C’)
INSERT INTO Lancamentos VALUES (1, ‘20090110’,25.00,‘C’)
INSERT INTO Lancamentos VALUES (1, ‘20090111’,10.00,‘D’)
INSERT INTO Lancamentos VALUES (2, ‘20090110’,15.00,‘D’)
INSERT INTO Lancamentos VALUES (2, ‘20090111’,26.00,‘D’)
INSERT INTO Lancamentos VALUES (3, ‘20090111’,19.00,‘C’)
INSERT INTO Lancamentos VALUES (4, ‘20090108’,42.00,‘D’)
INSERT INTO Lancamentos VALUES (4, ‘20090109’,33.00,‘C’)
INSERT INTO Lancamentos VALUES (4, ‘20090110’,18.00,‘C’)
INSERT INTO Lancamentos VALUES (4, ‘20090111’,11.00,‘D’)
INSERT INTO Lancamentos VALUES (5, ‘20090109’,23.00,‘C’)
INSERT INTO Lancamentos VALUES (5, ‘20090110’,55.00,‘C’)

O objetivo normalmente é muito simples. Para o script em questão é necessário obter o último lançamento para cada cliente. O último lançamento é identificado tendo-se por base a data mais recente. Já vi alguns exemplos incorretos, mas que esboçam uma primeira tentativa:

SELECT * FROM Lancamentos
WHERE Data = (SELECT MAX(Data) FROM Saldos)

SELECT IDCliente, MAX(Data) As UltimaData, Valor, Tipo
FROM Lancamentos
GROUP BY IDCliente, Valor, Tipo

SELECT IDCliente, MAX(Data) As UltimaData, Valor, Tipo
FROM Lancamentos
GROUP BY IDCliente

SELECT IDCliente, MAX(Data) As UltimaData, MAX(Valor) As UltimoValor,
MAX(Tipo) As UltimoTipo FROM Lancamentos
GROUP BY IDCliente

O primeiro SELECT está errado porque ele apenas retornará todos os lançamentos que ocorreram na última data e isso não necessariamente significa retornar o último lançamento para cada cliente, visto que podem haver clientes que tiveram lançamentos na última data (que é o caso do cliente 5).

O segundo SELECT está incorreto, porque a idéia é demonstrar o último lançamento por cliente. Da forma como a cláusula GROUP BY está formulada, será retornado o último lançamento pode cliente, por valor e por tipo. Como não há combinações cliente, valor e tipo repetidas, na prática essa cláusula não terá nenhum efeito e os 12 lançamentos serão retornados.

O terceiro SELECT até faz algum sentido, mas a cláusula GROUP BY exige que todos os campos que não estejam em uma função de agregação ou não sejam uma constante sejam contemplados. Assim, como Valor e Tipo não são utilizados por nenhuma função de agregação e nem representam constantes, essa instrução irá provocar um erro de sintaxe.

O último SELECT é uma tentativa de eliminar as restrições do terceiro SELECT, mas também não retorna os resultados corretamente. O MAX irá retornar o maior valor para uma determinada coluna e não necessariamente a maior data está associada ao maior valor. Considerando que existem apenas dois tipos de lançamento (C para crédito e D para débito), os clientes que possuírem débito terão o MAX(Tipo) igual a D e não necessariamente o último lançamento é um débito.

A utilização do MAX certamente faz parte da resposta, mas ela sozinha não é suficiente. O detalhe para encontrar o caminho da resposta está em perceber que o MAX é útil para encontrar a última data e com ele podemos encontrar a última data para cada cliente representando quando ocorreu seu último lançamento.

SELECT IDCliente, MAX(Data) As UltimaData
FROM Saldos GROUP BY IDCliente

O retorno da consulta é explicitado na tabela abaixo:

IDCliente UltimaData
1 2009-01-11 00:00:00
2 2009-01-11 00:00:00
3 2009-01-11 00:00:00
4 2009-01-11 00:00:00
5 2009-01-10 00:00:00

Uma vez que tenhamos o ID do cliente e a data do último lançamento (UltimaData), basta realizar um JOIN entre esse resultado e a tabela de lançamentos. Combinando esse resultado com a tabela de lançamentos através das colunas IDCliente e UltimaData com IDCliente e Data poderemos obter todos os detalhes do último lançamento.

— Utilização de Subqueries (Derived Table)
SELECT L1.* FROM Lancamentos As L1
INNER JOIN (
SELECT IDCliente, MAX(Data) As UltimaData
FROM Lancamentos GROUP BY IDCliente) As L2
ON L1.IDCliente = L2.IDCliente AND L1.Data = L2.UltimaData
ORDER BY L1.IDCliente

— Utilização de CTEs (2005 e superiores)
;WITH L2 As (
SELECT IDCliente, MAX(Data) As UltimaData
FROM Lancamentos GROUP BY IDCliente)

SELECT L1.* FROM Lancamentos As L1
INNER JOIN L2 ON L1.IDCliente = L2.IDCliente AND L1.Data = L2.UltimaData
ORDER BY L1.IDCliente

Ambas as construções retornam os últimos lançamentos de cada cliente na tabela de lançamentos

IDCliente Data Valor Tipo
1 2009-01-11 00:00:00 10,00 D
2 2009-01-11 00:00:00 26,00 D
3 2009-01-11 00:00:00 19,00 C
4 2009-01-11 00:00:00 11,00 D
5 2009-01-10 00:00:00 55,00 C

Caso seja necessário exibir o nome do cliente, basta combinar essa consulta com a tabela de clientes. Podemos perceber que o cliente 6 (Tatiana) não teve nenhum lançamento e por isso é interessante utilizar o LEFT OUTER JOIN. Mostrarei a sintaxe utilizando-me do recursos de CTEs disponível apenas para o SQL Server 2005 e posteriores. É possível resolver utilizando somente subqueries desde que o script seja adaptado.

— Definir a data do último lançamento por cliente
;WITH UL1 As (
SELECT IDCliente, MAX(Data) As UltimaData
FROM Lancamentos GROUP BY IDCliente),

— Recuperar todos os dados do último lançamento por cliente
UL2 As (
SELECT L.* FROM Lancamentos As L INNER JOIN UL1
ON L.IDCliente = UL1.IDCliente AND L.Data = UL1.UltimaData)

— Combinar os registros do último lançamento com a tabela de clientes
SELECT NomeCliente, Data, Valor, Tipo
FROM Clientes
LEFT OUTER JOIN UL2 ON Clientes.IDCliente = UL2.IDCliente

O comando retorna exatamente como o esperado (incluindo clientes que não possuam lançamentos):

NomeCliente Data Valor Tipo
Bianca 2009-01-11 00:00:00 10,00 D
Herbert 2009-01-11 00:00:00 26,00 D
Paloma 2009-01-11 00:00:00 19,00 C
Marcos 2009-01-11 00:00:00 11,00 D
Jonas 2009-01-10 00:00:00 55,00 C
Tatiana NULL NULL NULL

Para que a consulta funcione perfeitamente é necessário um pré-requisito que embora esteja presente na maioria das vezes em que esse tipo de dúvida aparece ele não é obrigatório. No exemplo em questão, estamos pressupondo que nunca haverá “empate”, ou seja, nunca dois registros serão eleitos como o último lançamento. Se colocarmos mais de um lançamento por dia teremos situações de empate e o resultado pode ser “negocialmente” incorreto.

— Insere um lancamento para o cliente 5
INSERT INTO Lancamentos VALUES (5, ‘20090110’,65.00,‘D’)

— Definir a data do último lançamento por cliente
;WITH UL1 As (
SELECT IDCliente, MAX(Data) As UltimaData
FROM Lancamentos GROUP BY IDCliente),

— Recuperar todos os dados do último lançamento por cliente
UL2 As (
SELECT L.* FROM Lancamentos As L INNER JOIN UL1
ON L.IDCliente = UL1.IDCliente AND L.Data = UL1.UltimaData)

— Combinar os registros do último lançamento com a tabela de clientes
SELECT NomeCliente, Data, Valor, Tipo
FROM Clientes
LEFT OUTER JOIN UL2 ON Clientes.IDCliente = UL2.IDCliente

O resultado inclui um lançamento para cada cliente, mas no caso do cliente 5 (Jonas) há dois lançamentos

NomeCliente Data Valor Tipo
Bianca 2009-01-11 00:00:00 10,00 D
Herbert 2009-01-11 00:00:00 26,00 D
Paloma 2009-01-11 00:00:00 19,00 C
Marcos 2009-01-11 00:00:00 11,00 D
Jonas 2009-01-10 00:00:00 55,00 C
Jonas 2009-01-10 00:00:00 65,00 D
Tatiana NULL NULL NULL

O cliente Jonas apareceu duas vezes por que o critério de último lançamento informa que o último lançamento é aquele cuja a data seja a maior. No caso do Jonas, há dois lançamentos com a maior data (10/01/2009). A premissa de que não haveria empate não foi obedecida. Se houvesse um terceiro registro no dia 10/01/2009, o problema se repetiria. Para eliminá-lo é necessário estipular um critério de desempate (Tie Breaker). É possível estipular algumas regras adicionais como o maior valor ou ainda optar-se pelo crédito no caso de haver empates relacionados a data do último lançamento. Essas regras adicionais diminuem ainda mais a ocorrência de empates, mas nada impediria que Jonas fizesse outro lançamento cujo valor fosse igual a R$ 65,00 e cujo tipo fosse crédito. Nesse caso o empate permaneceria.

O melhor Tiebreaker possível é a própria chave primária da tabela. Normalmente tabelas de lançamentos possui um identificador (ou sequencial). O script abaixo adiciona esse sequencial e o promove a chave primária da tabela.

— Adicionar uma coluna de identificação
ALTER TABLE Lancamentos ADD IDLancamento INT IDENTITY(1,1) NOT NULL

— Promove a coluna IDLancamento à chave primária
ALTER TABLE Lancamentos ADD CONSTRAINT PK_Lancamento PRIMARY KEY(IDLancamento)

Como a chave primária é única, não existe a possibilidade dela “empatar” e podemos utilizá-la como critério de desempate. A consulta abaixo, utiliza o critério de maior data como último lançamento, mas em caso de empate, a chave primária que for maior será utilizada para desempatar.

— Definir a data do último lançamento por cliente
;WITH UL1 As (
SELECT IDCliente, MAX(Data) As UltimaData
FROM Lancamentos GROUP BY IDCliente),

— Recuperar o maior IdLancamento da maior por cliente
UL2 As (
SELECT L.IDCliente, L.Data, MAX(IDLancamento) As UltimoIDLancamento
FROM Lancamentos As L
INNER JOIN UL1 ON L.IDCliente = UL1.IDCliente AND
L.Data = UL1.UltimaData
GROUP BY L.IDCliente, L.Data),

— Recuperar todos os dados do último lançamento por cliente
UL3 As (
SELECT L.* FROM Lancamentos As L INNER JOIN UL2
ON L.IDCliente = UL2.IDCliente AND L.Data = UL2.Data

— No caso de empate, vale o maior IDLancamento
AND L.IDLancamento = UL2.UltimoIDLancamento)

— Combinar os registros do último lançamento com a tabela de clientes
SELECT NomeCliente, Data, Valor, Tipo
FROM Clientes
LEFT OUTER JOIN UL3 ON Clientes.IDCliente = UL3.IDCliente

Como o cliente Jonas tem dois registros em sua maior data (10/01/2009), a chave primária serviu como critério de desempate retornando o registro com o maior IDLancamento (no caso o lançamento a débito de R$ 65,00).

NomeCliente Data Valor Tipo
Bianca 2009-01-11 00:00:00 10,00 D
Herbert 2009-01-11 00:00:00 26,00 D
Paloma 2009-01-11 00:00:00 19,00 C
Marcos 2009-01-11 00:00:00 11,00 D
Jonas 2009-01-10 00:00:00 65,00 D
Tatiana NULL NULL NULL

Se considerarmos que o IDLancamento é sequencial e que o maior ID sempre expressará o último lançamento, seria melhor utilizar o MAX(IDLancamento) ao invés do MAX(Data). Nesse caso, o IDLancamento que já é um ótimo Tiebreaker, também retornaria o ID do último lançamento.

— Definir a data do último lançamento por cliente
;WITH UL1 As (
SELECT IDCliente, MAX(IDLancamento) As UltimoLancamento
FROM Lancamentos GROUP BY IDCliente),

— Recuperar todos os dados do último lançamento por cliente
UL2 As (
SELECT L.* FROM Lancamentos As L INNER JOIN UL1
ON L.IDCliente = UL1.IDCliente AND L.IDLancamento = UL1.UltimoLancamento)

— Combinar os registros do último lançamento com a tabela de clientes
SELECT NomeCliente, Data, Valor, Tipo
FROM Clientes
LEFT OUTER JOIN UL2 ON Clientes.IDCliente = UL2.IDCliente

O resultado foi o mesmo que o apresentado anteriormente

NomeCliente Data Valor Tipo
Bianca 2009-01-11 00:00:00 10,00 D
Herbert 2009-01-11 00:00:00 26,00 D
Paloma 2009-01-11 00:00:00 19,00 C
Marcos 2009-01-11 00:00:00 11,00 D
Jonas 2009-01-10 00:00:00 65,00 D
Tatiana NULL NULL NULL

A consulta ficou bem mais simples e embora tenha descartado a necessidade de um tiebreaker, ela confiou na premissa de que o último IDLancamento sempre expressará o lançamento mais recente. Essa premissa é verdadeira na maioria dos casos, mas tenha em mente que lançamentos retroativos (ou atrasados) podem fazer com que ela não seja mais verdadeira e nesse caso a solução anterior seria a mais correta.

Todas essas considerações e scripts mostram como realizar consultas do tipo “o último registro pelo campo ?”, mas os novos recursos do Transact SQL a partir do SQL Server 2005 fazem com que tais scripts sejam desnecessários. Os operadores CROSS APPLY e OUTER APPLY são capazes de resolver esse problema de forma mais efetiva.

— Retorna o nome do cliente e seu último lançamento
— Considera apenas os clientes que possuam lançamentos
— As questões de TieBreaker estão devidamente tratadas

SELECT NomeCliente, Data, Valor, Tipo
FROM Clientes As C
CROSS APPLY (
SELECT TOP 1 Data, Valor, Tipo
FROM Lancamentos As L WHERE C.IDCliente = L.IDCliente
ORDER BY Data DESC, IDLancamento DESC) As UltimoLancamento

O resultado esperado é bem próximo, com exceção da cliente Tatiana que por não possuir nenhum lançamento não apareceu no resultado

NomeCliente Data Valor Tipo
Bianca 2009-01-11 00:00:00 10,00 D
Herbert 2009-01-11 00:00:00 26,00 D
Paloma 2009-01-11 00:00:00 19,00 C
Marcos 2009-01-11 00:00:00 11,00 D
Jonas 2009-01-10 00:00:00 65,00 D

O operador CROSS APPLY tem uma familiaridade muito próximo com o INNER JOIN (embora não sejam a mesma coisa) e por isso não considerou clientes sem lançamentos. A solução é utilizar o operador OUTER APPLY (que possui uma familiaridade com OUTER JOINs).

— Retorna o nome do cliente e seu último lançamento
— Considera apenas os clientes que possuam lançamentos
— As questões de TieBreaker estão devidamente tratadas

SELECT NomeCliente, Data, Valor, Tipo
FROM Clientes As C
OUTER APPLY (
SELECT TOP 1 Data, Valor, Tipo
FROM Lancamentos As L WHERE C.IDCliente = L.IDCliente
ORDER BY Data DESC, IDLancamento DESC) As UltimoLancamento

NomeCliente Data Valor Tipo
Bianca 2009-01-11 00:00:00 10,00 D
Herbert 2009-01-11 00:00:00 26,00 D
Paloma 2009-01-11 00:00:00 19,00 C
Marcos 2009-01-11 00:00:00 11,00 D
Jonas 2009-01-10 00:00:00 65,00 D
Tatiana NULL NULL NULL

O interessante desses novos operadores é que eles ajudam a resolver outros problemas que seriam muito mais trabalhosos de fazer. Se ao invés do último lançamento fosse necessário os dois últimos lançamentos, as abordagens anteriores que se baseavam no MAX seriam invalidadas enquanto que com os operadores CROSS APPLY e OUTER APPLY bastaria utilizar um TOP 2 ao invés de TOP 1. Por essas questões é que considerar a utilização de novos recursos é algo que deve ser observado.

O objetivo desse posts foi levar esclarescimentos a uma dúvida que nos últimos dias tem se tornado tão freqüente. Espero que os que a possuam e que leram esse post não tenham mais dificuldades na elaboração desse tipo de consulta.

[ ]s,

Gustavo

fonte: https://gustavomaiaaguiar.wordpress.com/2009/01/11/como-retornar-o-ultimo-registro-por-grupo/